| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #ifndef _EDGE_IMPULSE_RUN_CLASSIFIER_H_ |
| #define _EDGE_IMPULSE_RUN_CLASSIFIER_H_ |
|
|
| #include "ei_model_types.h" |
| #include "model-parameters/model_metadata.h" |
|
|
| #include "ei_run_dsp.h" |
| #include "ei_classifier_types.h" |
| #include "ei_signal_with_axes.h" |
| #include "postprocessing/ei_postprocessing.h" |
| #include "edge-impulse-sdk/classifier/ei_data_normalization.h" |
| #include "edge-impulse-sdk/classifier/ei_print_results.h" |
|
|
| #include "edge-impulse-sdk/porting/ei_classifier_porting.h" |
| #include "edge-impulse-sdk/porting/ei_logging.h" |
| #include <memory> |
|
|
| #if EI_CLASSIFIER_LOAD_ANOMALY_H |
| #include "inferencing_engines/anomaly.h" |
| #endif |
|
|
| #if defined(EI_CLASSIFIER_HAS_SAMPLER) && EI_CLASSIFIER_HAS_SAMPLER == 1 |
| #include "ei_sampler.h" |
| #endif |
|
|
| #if (EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_TFLITE) && (EI_CLASSIFIER_COMPILED != 1) |
| #include "edge-impulse-sdk/classifier/inferencing_engines/tflite_micro.h" |
| #elif EI_CLASSIFIER_COMPILED == 1 |
| #include "edge-impulse-sdk/classifier/inferencing_engines/tflite_eon.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_TFLITE_FULL |
| #include "edge-impulse-sdk/classifier/inferencing_engines/tflite_full.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_TFLITE_TIDL |
| #include "edge-impulse-sdk/classifier/inferencing_engines/tflite_tidl.h" |
| #elif (EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_TENSORRT) |
| #include "edge-impulse-sdk/classifier/inferencing_engines/tensorrt.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_TENSAIFLOW |
| #include "edge-impulse-sdk/classifier/inferencing_engines/tensaiflow.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_DRPAI |
| #include "edge-impulse-sdk/classifier/inferencing_engines/drpai.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_AKIDA |
| #include "edge-impulse-sdk/classifier/inferencing_engines/akida.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_ONNX_TIDL |
| #include "edge-impulse-sdk/classifier/inferencing_engines/onnx_tidl.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_MEMRYX |
| #include "edge-impulse-sdk/classifier/inferencing_engines/memryx.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_ETHOS_LINUX |
| #include "edge-impulse-sdk/classifier/inferencing_engines/ethos_linux.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_ATON |
| #include "edge-impulse-sdk/classifier/inferencing_engines/aton.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_CEVA_NPN |
| #include "edge-impulse-sdk/classifier/inferencing_engines/ceva_npn.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_VLM_CONNECTOR |
| #include "edge-impulse-sdk/classifier/inferencing_engines/vlm_connector.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_NORDIC_AXON |
| #include "edge-impulse-sdk/classifier/inferencing_engines/nordic_axon.h" |
| #elif EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_NONE |
| |
| #else |
| #error "Unknown inferencing engine" |
| #endif |
|
|
| |
| #include "model-parameters/model_variables.h" |
|
|
| #ifdef __cplusplus |
| namespace { |
| #endif |
|
|
| |
| extern "C" EI_IMPULSE_ERROR run_inference(ei_impulse_handle_t *handle, ei_feature_t *fmatrix, ei_impulse_result_t *result, bool debug); |
| extern "C" EI_IMPULSE_ERROR run_classifier_image_quantized(const ei_impulse_t *impulse, signal_t *signal, ei_impulse_result_t *result, bool debug); |
| static EI_IMPULSE_ERROR can_run_classifier_image_quantized(const ei_impulse_t *impulse, ei_learning_block_t block_ptr); |
| static void ei_result_struct_timing_us_to_ms(ei_impulse_result_t *result); |
|
|
| #if EI_CLASSIFIER_LOAD_IMAGE_SCALING |
| EI_IMPULSE_ERROR ei_scale_fmatrix(ei_learning_block_t *block, ei::matrix_t *fmatrix); |
| EI_IMPULSE_ERROR ei_unscale_fmatrix(ei_learning_block_t *block, ei::matrix_t *fmatrix); |
| #endif |
|
|
| |
|
|
| static uint64_t classifier_continuous_features_written = 0; |
|
|
| |
|
|
| |
| |
|
|
| |
| |
| |
| |
| |
| __attribute__((unused)) void display_results(ei_impulse_handle_t *handle, ei_impulse_result_t* result) |
| { |
| ei_print_results(handle, result); |
| display_postprocessing(handle, result); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| extern "C" EI_IMPULSE_ERROR run_inference( |
| ei_impulse_handle_t *handle, |
| ei_feature_t *fmatrix, |
| ei_impulse_result_t *result, |
| bool debug = false) |
| { |
| auto& impulse = handle->impulse; |
| for (size_t ix = 0; ix < impulse->learning_blocks_size; ix++) { |
|
|
| ei_learning_block_t block = impulse->learning_blocks[ix]; |
|
|
| #if EI_CLASSIFIER_LOAD_IMAGE_SCALING |
| auto start_scale_matrix_us = ei_read_timer_us(); |
|
|
| |
| |
| EI_IMPULSE_ERROR scale_res = ei_scale_fmatrix(&block, fmatrix[0].matrix); |
| if (scale_res != EI_IMPULSE_OK) { |
| return scale_res; |
| } |
|
|
| auto end_scale_matrix_us = ei_read_timer_us(); |
| #endif |
|
|
| EI_IMPULSE_ERROR res = block.infer_fn(impulse, fmatrix, ix, (uint32_t*)block.input_block_ids, block.input_block_ids_size, result, block.config, debug); |
| if (res != EI_IMPULSE_OK) { |
| return res; |
| } |
|
|
| #if EI_CLASSIFIER_LOAD_IMAGE_SCALING |
| auto start_unscale_matrix_us = ei_read_timer_us(); |
|
|
| |
| if (impulse->learning_blocks_size > 1) { |
| scale_res = ei_unscale_fmatrix(&block, fmatrix[0].matrix); |
| if (scale_res != EI_IMPULSE_OK) { |
| return scale_res; |
| } |
| } |
|
|
| auto end_unscale_matrix_us = ei_read_timer_us(); |
|
|
| |
| result->timing.dsp_us += (end_unscale_matrix_us - start_unscale_matrix_us) + |
| (end_scale_matrix_us - start_scale_matrix_us); |
| #endif |
| } |
|
|
| if (ei_run_impulse_check_canceled() == EI_IMPULSE_CANCELED) { |
| return EI_IMPULSE_CANCELED; |
| } |
|
|
| return EI_IMPULSE_OK; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| extern "C" EI_IMPULSE_ERROR process_impulse(ei_impulse_handle_t *handle, |
| signal_t *signal, |
| ei_impulse_result_t *result, |
| bool debug = false) |
| { |
| if ((handle == nullptr) || (handle->impulse == nullptr) || (result == nullptr) || (signal == nullptr)) { |
| return EI_IMPULSE_INFERENCE_ERROR; |
| } |
|
|
| memset(result, 0, sizeof(ei_impulse_result_t)); |
|
|
| #if EI_IMPULSE_RESULT_CLASSIFICATION_IS_STATICALLY_ALLOCATED == 0 |
| static std::vector<ei_impulse_result_classification_t> classification_results; |
| classification_results.clear(); |
|
|
| if (handle->impulse->results_type == EI_CLASSIFIER_TYPE_CLASSIFICATION || |
| handle->impulse->results_type == EI_CLASSIFIER_TYPE_REGRESSION) { |
| #ifdef EI_DSP_RESULT_OVERRIDE |
| for (size_t ix = 0; ix < EI_DSP_RESULT_OVERRIDE; ix++) { |
| ei_impulse_result_classification_t classification = { |
| .label = "", |
| .value = 0.0f |
| }; |
| classification_results.push_back(classification); |
| } |
| #else |
| for (size_t ix = 0; ix < handle->impulse->label_count; ix++) { |
| ei_impulse_result_classification_t classification = { |
| .label = handle->impulse->categories[ix], |
| .value = 0.0f |
| }; |
| classification_results.push_back(classification); |
| } |
| #endif |
| } |
|
|
| result->classification = classification_results.data(); |
| #endif |
|
|
| uint8_t num_results = handle->impulse->output_tensors_size; |
|
|
| std::unique_ptr<ei_feature_t[]> raw_results_ptr(new ei_feature_t[num_results]); |
|
|
| result->_raw_outputs = raw_results_ptr.get(); |
| memset(result->_raw_outputs, 0, sizeof(ei_feature_t) * num_results); |
|
|
| EI_IMPULSE_ERROR res = EI_IMPULSE_OK; |
| (void)res; |
|
|
| #if (EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_VLM_CONNECTOR) |
| |
| res = run_vlm_inference(handle, signal, 0, result, handle->impulse->learning_blocks[0].config, false); |
| if (res != EI_IMPULSE_OK) { |
| return res; |
| } |
| res = run_postprocessing(handle, result); |
| return res; |
| #endif |
| #if (EI_CLASSIFIER_QUANTIZATION_ENABLED == 1 && (EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_TFLITE || EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_TENSAIFLOW || EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_ONNX_TIDL) || EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_DRPAI || EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_ATON) |
| |
| ei_learning_block_t block = handle->impulse->learning_blocks[0]; |
| if (can_run_classifier_image_quantized(handle->impulse, block) == EI_IMPULSE_OK) { |
| res = run_classifier_image_quantized(handle->impulse, signal, result, debug); |
| if (res != EI_IMPULSE_OK) { |
| return res; |
| } |
| res = run_postprocessing(handle, result); |
| ei_result_struct_timing_us_to_ms(result); |
| return res; |
| } |
| #endif |
| uint32_t block_num = handle->impulse->dsp_blocks_size; |
|
|
| |
| std::unique_ptr<ei_feature_t[]> features_ptr(new ei_feature_t[block_num]); |
| ei_feature_t* features = features_ptr.get(); |
|
|
| if (features == nullptr) { |
| ei_printf("ERR: Out of memory, can't allocate features\n"); |
| return EI_IMPULSE_ALLOC_FAILED; |
| } |
|
|
| memset(features, 0, sizeof(ei_feature_t) * block_num); |
|
|
| |
| std::unique_ptr<std::unique_ptr<ei::matrix_t>[]> matrix_ptrs_ptr(new std::unique_ptr<ei::matrix_t>[block_num]); |
| std::unique_ptr<ei::matrix_t> *matrix_ptrs = matrix_ptrs_ptr.get(); |
|
|
| if (matrix_ptrs == nullptr) { |
| delete[] matrix_ptrs; |
| ei_printf("ERR: Out of memory, can't allocate matrix_ptrs\n"); |
| return EI_IMPULSE_ALLOC_FAILED; |
| } |
|
|
| uint64_t dsp_start_us = ei_read_timer_us(); |
|
|
| size_t out_features_index = 0; |
|
|
| for (size_t ix = 0; ix < handle->impulse->dsp_blocks_size; ix++) { |
| ei_model_dsp_t block = handle->impulse->dsp_blocks[ix]; |
|
|
| matrix_ptrs[ix] = std::unique_ptr<ei::matrix_t>(new ei::matrix_t(1, block.n_output_features)); |
| if (matrix_ptrs[ix] == nullptr) { |
| ei_printf("ERR: Out of memory, can't allocate matrix_ptrs[%lu]\n", (unsigned long)ix); |
| return EI_IMPULSE_ALLOC_FAILED; |
| } |
|
|
| if (matrix_ptrs[ix]->buffer == nullptr) { |
| ei_printf("ERR: Out of memory, can't allocate matrix_ptrs[%lu]\n", (unsigned long)ix); |
| delete[] matrix_ptrs; |
| return EI_IMPULSE_ALLOC_FAILED; |
| } |
|
|
| features[ix].matrix = matrix_ptrs[ix].get(); |
| features[ix].blockId = block.blockId; |
|
|
| if (out_features_index + block.n_output_features > handle->impulse->nn_input_frame_size) { |
| ei_printf("ERR: Would write outside feature buffer\n"); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
|
|
| #if EIDSP_SIGNAL_C_FN_POINTER |
| if (block.axes_size != handle->impulse->raw_samples_per_frame) { |
| ei_printf("ERR: EIDSP_SIGNAL_C_FN_POINTER can only be used when all axes are selected for DSP blocks\n"); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
| auto internal_signal = signal; |
| #else |
| SignalWithAxes swa(signal, block.axes, block.axes_size, handle->impulse); |
| auto internal_signal = swa.get_signal(); |
| #endif |
|
|
| int ret; |
| if (block.factory) { |
| |
| static bool has_printed = false; |
| if (!has_printed) { |
| EI_LOGI("Impulse maintains state. Call run_classifier_init() to reset state (e.g. if data stream is interrupted.)\n"); |
| has_printed = true; |
| } |
|
|
| |
| auto dsp_handle = handle->state.get_dsp_handle(ix); |
| if(dsp_handle) { |
| ret = dsp_handle->extract( |
| internal_signal, |
| features[ix].matrix, |
| block.config, |
| handle->impulse->frequency, |
| result); |
| } |
| else { |
| return EI_IMPULSE_OUT_OF_MEMORY; |
| } |
| } else { |
| ret = block.extract_fn(internal_signal, features[ix].matrix, block.config, handle->impulse->frequency); |
| } |
|
|
| if (ret != EIDSP_OK) { |
| ei_printf("ERR: Failed to run DSP process (%d)\n", ret); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
|
|
| if (ei_run_impulse_check_canceled() == EI_IMPULSE_CANCELED) { |
| return EI_IMPULSE_CANCELED; |
| } |
|
|
| out_features_index += block.n_output_features; |
| } |
|
|
| #if EI_CLASSIFIER_HAS_DATA_NORMALIZATION |
| EI_IMPULSE_ERROR dn_error = run_data_normalization(handle, features); |
| if (dn_error != EI_IMPULSE_OK) { |
| ei_printf("ERR: Failed to run Data Normalization process (%d)\n", dn_error); |
| return dn_error; |
| } |
| #endif |
|
|
| result->timing.dsp_us = ei_read_timer_us() - dsp_start_us; |
|
|
| if (debug) { |
| ei_printf("Features (%d ms.): ", result->timing.dsp); |
| for (size_t ix = 0; ix < block_num; ix++) { |
| if (features[ix].matrix == nullptr) { |
| continue; |
| } |
| for (size_t jx = 0; jx < features[ix].matrix->cols; jx++) { |
| ei_printf_float(features[ix].matrix->buffer[jx]); |
| ei_printf(" "); |
| } |
| ei_printf("\n"); |
| } |
| } |
|
|
| if (debug) { |
| ei_printf("Running impulse...\n"); |
| } |
|
|
| #if EI_CLASSIFIER_DSP_ONLY |
| ei_result_struct_timing_us_to_ms(result); |
|
|
| return EI_IMPULSE_OK; |
| #else |
| res = run_inference(handle, features, result, debug); |
| if (res != EI_IMPULSE_OK) { |
| return res; |
| } |
|
|
| res = run_postprocessing(handle, result); |
| if (res != EI_IMPULSE_OK) { |
| return res; |
| } |
|
|
| ei_result_struct_timing_us_to_ms(result); |
|
|
| return EI_IMPULSE_OK; |
| #endif |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| extern "C" EI_IMPULSE_ERROR init_impulse(ei_impulse_handle_t *handle) { |
| if (!handle) { |
| return EI_IMPULSE_OUT_OF_MEMORY; |
| } |
| handle->state.reset(); |
| return EI_IMPULSE_OK; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| extern "C" EI_IMPULSE_ERROR process_impulse_continuous(ei_impulse_handle_t *handle, |
| signal_t *signal, |
| ei_impulse_result_t *result, |
| bool debug = false) |
| { |
| if ((handle == nullptr) || (handle->impulse == nullptr) || (result == nullptr) || (signal == nullptr)) { |
| return EI_IMPULSE_INFERENCE_ERROR; |
| } |
|
|
| memset(result, 0, sizeof(ei_impulse_result_t)); |
|
|
| #if EI_IMPULSE_RESULT_CLASSIFICATION_IS_STATICALLY_ALLOCATED == 0 |
| static std::vector<ei_impulse_result_classification_t> classification_results; |
| classification_results.clear(); |
|
|
| if (handle->impulse->results_type == EI_CLASSIFIER_TYPE_CLASSIFICATION || |
| handle->impulse->results_type == EI_CLASSIFIER_TYPE_REGRESSION) { |
| #ifdef EI_DSP_RESULT_OVERRIDE |
| for (size_t ix = 0; ix < EI_DSP_RESULT_OVERRIDE; ix++) { |
| ei_impulse_result_classification_t classification = { |
| .label = "", |
| .value = 0.0f |
| }; |
| classification_results.push_back(classification); |
| } |
| #else |
| for (size_t ix = 0; ix < handle->impulse->label_count; ix++) { |
| ei_impulse_result_classification_t classification = { |
| .label = handle->impulse->categories[ix], |
| .value = 0.0f |
| }; |
| classification_results.push_back(classification); |
| } |
| #endif |
| } |
|
|
| result->classification = classification_results.data(); |
|
|
| #else |
|
|
| for (int i = 0; i < handle->impulse->label_count; i++) { |
| |
| result->classification[i].label = handle->impulse->categories[(uint32_t)i]; |
| } |
|
|
| #endif |
|
|
| |
| std::unique_ptr<ei_feature_t[]> raw_results_ptr(new ei_feature_t[handle->impulse->learning_blocks_size]); |
| result->_raw_outputs = raw_results_ptr.get(); |
| memset(result->_raw_outputs, 0, sizeof(ei_feature_t) * handle->impulse->learning_blocks_size); |
|
|
| auto impulse = handle->impulse; |
| static ei::matrix_t static_features_matrix(1, impulse->nn_input_frame_size); |
| if (!static_features_matrix.buffer) { |
| return EI_IMPULSE_ALLOC_FAILED; |
| } |
|
|
| EI_IMPULSE_ERROR ei_impulse_error = EI_IMPULSE_OK; |
|
|
| uint64_t dsp_start_us = ei_read_timer_us(); |
|
|
| size_t out_features_index = 0; |
|
|
| for (size_t ix = 0; ix < impulse->dsp_blocks_size; ix++) { |
| ei_model_dsp_t block = impulse->dsp_blocks[ix]; |
|
|
| if (out_features_index + block.n_output_features > impulse->nn_input_frame_size) { |
| ei_printf("ERR: Would write outside feature buffer\n"); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
|
|
| ei::matrix_t fm(1, block.n_output_features, |
| static_features_matrix.buffer + out_features_index); |
|
|
| int (*extract_fn_slice)(ei::signal_t *signal, ei::matrix_t *output_matrix, void *config, const float frequency, matrix_size_t *out_matrix_size); |
|
|
| |
| if (block.extract_fn == extract_mfcc_features) { |
| extract_fn_slice = &extract_mfcc_per_slice_features; |
| } |
| else if (block.extract_fn == extract_spectrogram_features) { |
| extract_fn_slice = &extract_spectrogram_per_slice_features; |
| } |
| else if (block.extract_fn == extract_mfe_features) { |
| extract_fn_slice = &extract_mfe_per_slice_features; |
| } |
| else { |
| ei_printf("ERR: Unknown extract function, only MFCC, MFE and spectrogram supported\n"); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
|
|
| matrix_size_t features_written; |
|
|
| #if EIDSP_SIGNAL_C_FN_POINTER |
| if (block.axes_size != impulse->raw_samples_per_frame) { |
| ei_printf("ERR: EIDSP_SIGNAL_C_FN_POINTER can only be used when all axes are selected for DSP blocks\n"); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
| int ret = extract_fn_slice(signal, &fm, block.config, impulse->frequency, &features_written); |
| #else |
| SignalWithAxes swa(signal, block.axes, block.axes_size, impulse); |
| int ret = extract_fn_slice(swa.get_signal(), &fm, block.config, impulse->frequency, &features_written); |
| #endif |
|
|
| if (ret != EIDSP_OK) { |
| ei_printf("ERR: Failed to run DSP process (%d)\n", ret); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
|
|
| if (ei_run_impulse_check_canceled() == EI_IMPULSE_CANCELED) { |
| return EI_IMPULSE_CANCELED; |
| } |
|
|
| classifier_continuous_features_written += (features_written.rows * features_written.cols); |
|
|
| out_features_index += block.n_output_features; |
| } |
|
|
| result->timing.dsp_us = ei_read_timer_us() - dsp_start_us; |
|
|
| if (classifier_continuous_features_written >= impulse->nn_input_frame_size) { |
| dsp_start_us = ei_read_timer_us(); |
|
|
| uint32_t block_num = impulse->dsp_blocks_size + impulse->learning_blocks_size; |
|
|
| |
| std::unique_ptr<ei_feature_t[]> features_ptr(new ei_feature_t[block_num]); |
| ei_feature_t* features = features_ptr.get(); |
| if (features == nullptr) { |
| ei_printf("ERR: Out of memory, can't allocate features\n"); |
| return EI_IMPULSE_ALLOC_FAILED; |
| } |
| memset(features, 0, sizeof(ei_feature_t) * block_num); |
|
|
| |
| std::unique_ptr<ei::matrix_t> *matrix_ptrs = new std::unique_ptr<ei::matrix_t>[block_num]; |
| if (matrix_ptrs == nullptr) { |
| ei_printf("ERR: Out of memory, can't allocate matrix_ptrs\n"); |
| return EI_IMPULSE_ALLOC_FAILED; |
| } |
|
|
| out_features_index = 0; |
| |
| for (size_t ix = 0; ix < impulse->dsp_blocks_size; ix++) { |
| ei_model_dsp_t block = impulse->dsp_blocks[ix]; |
| matrix_ptrs[ix] = std::unique_ptr<ei::matrix_t>(new ei::matrix_t(1, block.n_output_features)); |
|
|
| if (matrix_ptrs[ix] == nullptr) { |
| ei_printf("ERR: Out of memory, can't allocate matrix_ptrs[%lu]\n", (unsigned long)ix); |
| return EI_IMPULSE_ALLOC_FAILED; |
| } |
|
|
| if (matrix_ptrs[ix]->buffer == nullptr) { |
| ei_printf("ERR: Out of memory, can't allocate matrix_ptrs[%lu]\n", (unsigned long)ix); |
| delete[] matrix_ptrs; |
| return EI_IMPULSE_ALLOC_FAILED; |
| } |
|
|
| features[ix].matrix = matrix_ptrs[ix].get(); |
| features[ix].blockId = block.blockId; |
|
|
| |
| for (size_t m_ix = 0; m_ix < block.n_output_features; m_ix++) { |
| features[ix].matrix->buffer[m_ix] = static_features_matrix.buffer[out_features_index + m_ix]; |
| } |
|
|
| if (block.extract_fn == extract_mfcc_features) { |
| calc_cepstral_mean_and_var_normalization_mfcc(features[ix].matrix, block.config); |
| } |
| else if (block.extract_fn == extract_spectrogram_features) { |
| calc_cepstral_mean_and_var_normalization_spectrogram(features[ix].matrix, block.config); |
| } |
| else if (block.extract_fn == extract_mfe_features) { |
| calc_cepstral_mean_and_var_normalization_mfe(features[ix].matrix, block.config); |
| } |
| out_features_index += block.n_output_features; |
| } |
|
|
| result->timing.dsp_us += ei_read_timer_us() - dsp_start_us; |
|
|
| if (debug) { |
| ei_printf("Feature Matrix: \n"); |
| for (size_t ix = 0; ix < features->matrix->cols; ix++) { |
| ei_printf_float(features->matrix->buffer[ix]); |
| ei_printf(" "); |
| } |
| ei_printf("\n"); |
| ei_printf("Running impulse...\n"); |
| } |
|
|
| ei_impulse_error = run_inference(handle, features, result, debug); |
| if (ei_impulse_error != EI_IMPULSE_OK) { |
| return ei_impulse_error; |
| } |
| delete[] matrix_ptrs; |
| ei_impulse_error = run_postprocessing(handle, result); |
| if (ei_impulse_error != EI_IMPULSE_OK) { |
| return ei_impulse_error; |
| } |
| } |
|
|
| ei_result_struct_timing_us_to_ms(result); |
|
|
| return ei_impulse_error; |
| } |
|
|
| |
| |
| |
| __attribute__((unused)) static EI_IMPULSE_ERROR can_run_classifier_image_quantized(const ei_impulse_t *impulse, ei_learning_block_t block_ptr) { |
|
|
| if (impulse->inferencing_engine != EI_CLASSIFIER_TFLITE |
| && impulse->inferencing_engine != EI_CLASSIFIER_TENSAIFLOW |
| && impulse->inferencing_engine != EI_CLASSIFIER_DRPAI |
| && impulse->inferencing_engine != EI_CLASSIFIER_ONNX_TIDL |
| && impulse->inferencing_engine != EI_CLASSIFIER_ATON) |
| { |
| return EI_IMPULSE_UNSUPPORTED_INFERENCING_ENGINE; |
| } |
|
|
| |
| if (impulse->has_anomaly){ |
| return EI_IMPULSE_ONLY_SUPPORTED_FOR_IMAGES; |
| } |
|
|
| |
| if (block_ptr.infer_fn != run_nn_inference) { |
| return EI_IMPULSE_ONLY_SUPPORTED_FOR_IMAGES; |
| } |
|
|
| |
| ei_learning_block_config_tflite_graph_t *block_config = (ei_learning_block_config_tflite_graph_t*)block_ptr.config; |
| if (block_config->quantized != 1) { |
| return EI_IMPULSE_ONLY_SUPPORTED_FOR_IMAGES; |
| } |
|
|
| |
| if (impulse->dsp_blocks_size != 1 || impulse->dsp_blocks[0].extract_fn != extract_image_features) { |
| return EI_IMPULSE_ONLY_SUPPORTED_FOR_IMAGES; |
| } |
|
|
| return EI_IMPULSE_OK; |
| } |
|
|
| #if EI_CLASSIFIER_QUANTIZATION_ENABLED == 1 && (EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_TFLITE || EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_TENSAIFLOW || EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_DRPAI || EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_ONNX_TIDL || EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_ATON) |
|
|
| |
| |
| |
| |
| |
| extern "C" EI_IMPULSE_ERROR run_classifier_image_quantized( |
| const ei_impulse_t *impulse, |
| signal_t *signal, |
| ei_impulse_result_t *result, |
| bool debug = false) |
| { |
| return run_nn_inference_image_quantized(impulse, signal, 0, result, impulse->learning_blocks[0].config, debug); |
| } |
|
|
| #endif |
|
|
| #if EI_CLASSIFIER_LOAD_IMAGE_SCALING |
| static const float torch_mean[] = { 0.485, 0.456, 0.406 }; |
| static const float torch_std[] = { 0.229, 0.224, 0.225 }; |
| |
| static const float tao_mean[] = { 103.939, 116.779, 123.68 }; |
|
|
| EI_IMPULSE_ERROR ei_scale_fmatrix(ei_learning_block_t *block, ei::matrix_t *fmatrix) { |
| if (block->image_scaling == EI_CLASSIFIER_IMAGE_SCALING_TORCH) { |
| |
| for (size_t ix = 0; ix < fmatrix->rows * fmatrix->cols; ix += 3) { |
| fmatrix->buffer[ix + 0] = (fmatrix->buffer[ix + 0] - torch_mean[0]) / torch_std[0]; |
| fmatrix->buffer[ix + 1] = (fmatrix->buffer[ix + 1] - torch_mean[1]) / torch_std[1]; |
| fmatrix->buffer[ix + 2] = (fmatrix->buffer[ix + 2] - torch_mean[2]) / torch_std[2]; |
| } |
| } |
| else if (block->image_scaling == EI_CLASSIFIER_IMAGE_SCALING_0_255) { |
| int scale_res = numpy::scale(fmatrix, 255.0f); |
| if (scale_res != EIDSP_OK) { |
| ei_printf("ERR: Failed to scale matrix (%d)\n", scale_res); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
| } |
| else if (block->image_scaling == EI_CLASSIFIER_IMAGE_SCALING_MIN128_127) { |
| int scale_res = numpy::scale_and_add(fmatrix, 255.0f, -128.0f); |
| if (scale_res != EIDSP_OK) { |
| ei_printf("ERR: Failed to scale matrix (%d)\n", scale_res); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
| } |
| else if (block->image_scaling == EI_CLASSIFIER_IMAGE_SCALING_MIN1_1) { |
| int scale_res = numpy::scale_and_add(fmatrix, 2.0f, -1.0f); |
| if (scale_res != EIDSP_OK) { |
| ei_printf("ERR: Failed to scale matrix (%d)\n", scale_res); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
| } |
| else if (block->image_scaling == EI_CLASSIFIER_IMAGE_SCALING_BGR_SUBTRACT_IMAGENET_MEAN) { |
| int scale_res = numpy::scale(fmatrix, 255.0f); |
| if (scale_res != EIDSP_OK) { |
| ei_printf("ERR: Failed to scale matrix (%d)\n", scale_res); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
| |
| for (size_t ix = 0; ix < fmatrix->rows * fmatrix->cols; ix += 3) { |
| float r = fmatrix->buffer[ix + 0]; |
| fmatrix->buffer[ix + 0] = fmatrix->buffer[ix + 2] - tao_mean[0]; |
| fmatrix->buffer[ix + 1] -= tao_mean[1]; |
| fmatrix->buffer[ix + 2] = r - tao_mean[2]; |
| } |
| } |
|
|
| return EI_IMPULSE_OK; |
| } |
|
|
| EI_IMPULSE_ERROR ei_unscale_fmatrix(ei_learning_block_t *block, ei::matrix_t *fmatrix) { |
| if (block->image_scaling == EI_CLASSIFIER_IMAGE_SCALING_TORCH) { |
| |
| for (size_t ix = 0; ix < fmatrix->rows * fmatrix->cols; ix += 3) { |
| fmatrix->buffer[ix + 0] = (fmatrix->buffer[ix + 0] * torch_std[0]) + torch_mean[0]; |
| fmatrix->buffer[ix + 1] = (fmatrix->buffer[ix + 1] * torch_std[1]) + torch_mean[1]; |
| fmatrix->buffer[ix + 2] = (fmatrix->buffer[ix + 2] * torch_std[2]) + torch_mean[2]; |
| } |
| } |
| else if (block->image_scaling == EI_CLASSIFIER_IMAGE_SCALING_MIN128_127) { |
| int scale_res = numpy::scale_and_add(fmatrix, 1.0f / 255.0f, 128.0f / 255.0f); |
| if (scale_res != EIDSP_OK) { |
| ei_printf("ERR: Failed to scale matrix (%d)\n", scale_res); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
| } |
| else if (block->image_scaling == EI_CLASSIFIER_IMAGE_SCALING_MIN1_1) { |
| int scale_res = numpy::scale_and_add(fmatrix, 1.0f / 2.0f, 1.0f / 2.0f); |
| if (scale_res != EIDSP_OK) { |
| ei_printf("ERR: Failed to scale matrix (%d)\n", scale_res); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
| } |
| else if (block->image_scaling == EI_CLASSIFIER_IMAGE_SCALING_0_255) { |
| int scale_res = numpy::scale(fmatrix, 1 / 255.0f); |
| if (scale_res != EIDSP_OK) { |
| ei_printf("ERR: Failed to scale matrix (%d)\n", scale_res); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
| } |
| else if (block->image_scaling == EI_CLASSIFIER_IMAGE_SCALING_BGR_SUBTRACT_IMAGENET_MEAN) { |
| |
| for (size_t ix = 0; ix < fmatrix->rows * fmatrix->cols; ix += 3) { |
| float b = fmatrix->buffer[ix + 0]; |
| fmatrix->buffer[ix + 0] = fmatrix->buffer[ix + 2] + tao_mean[2]; |
| fmatrix->buffer[ix + 1] += tao_mean[1]; |
| fmatrix->buffer[ix + 2] = b + tao_mean[0]; |
| } |
| int scale_res = numpy::scale(fmatrix, 1 / 255.0f); |
| if (scale_res != EIDSP_OK) { |
| ei_printf("ERR: Failed to scale matrix (%d)\n", scale_res); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
| } |
| return EI_IMPULSE_OK; |
| } |
| #endif |
|
|
| |
| |
| |
| |
| static void ei_result_struct_timing_us_to_ms(ei_impulse_result_t *result) { |
| |
| |
| |
|
|
| result->timing.dsp = (int)((result->timing.dsp_us + 500) / 1000); |
| result->timing.classification = (int)((result->timing.classification_us + 500) / 1000); |
| result->timing.anomaly = (int)((result->timing.anomaly_us + 500) / 1000); |
| result->timing.postprocessing = (int)((result->timing.postprocessing_us + 500) / 1000); |
| } |
|
|
| |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| extern "C" void run_classifier_init(void) |
| { |
|
|
| classifier_continuous_features_written = 0; |
| ei_dsp_clear_continuous_audio_state(); |
| init_impulse(&ei_default_impulse); |
| init_postprocessing(&ei_default_impulse); |
| #if EI_CLASSIFIER_HAS_DATA_NORMALIZATION |
| init_data_normalization(&ei_default_impulse); |
| #endif |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| __attribute__((unused)) void run_classifier_init(ei_impulse_handle_t *handle) |
| { |
| classifier_continuous_features_written = 0; |
| ei_dsp_clear_continuous_audio_state(); |
| init_impulse(handle); |
| init_postprocessing(handle); |
| #if EI_CLASSIFIER_HAS_DATA_NORMALIZATION |
| init_data_normalization(handle); |
| #endif |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| extern "C" void run_classifier_deinit(void) |
| { |
| deinit_postprocessing(&ei_default_impulse); |
| } |
|
|
| __attribute__((unused)) void run_classifier_deinit(ei_impulse_handle_t *handle) |
| { |
| deinit_postprocessing(handle); |
| #if EI_CLASSIFIER_HAS_DATA_NORMALIZATION |
| deinit_data_normalization(handle); |
| #endif |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| extern "C" EI_IMPULSE_ERROR run_classifier_continuous( |
| signal_t *signal, |
| ei_impulse_result_t *result, |
| bool debug = false, |
| bool enable_maf_unused = true) |
| { |
| auto& impulse = ei_default_impulse; |
| return process_impulse_continuous(&impulse, signal, result, debug); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| __attribute__((unused)) EI_IMPULSE_ERROR run_classifier_continuous( |
| ei_impulse_handle_t *impulse, |
| signal_t *signal, |
| ei_impulse_result_t *result, |
| bool debug = false, |
| bool enable_maf_unused = true) |
| { |
| return process_impulse_continuous(impulse, signal, result, debug); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| extern "C" EI_IMPULSE_ERROR run_classifier( |
| signal_t *signal, |
| ei_impulse_result_t *result, |
| bool debug = false) |
| { |
| return process_impulse(&ei_default_impulse, signal, result, debug); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| __attribute__((unused)) EI_IMPULSE_ERROR run_classifier( |
| ei_impulse_handle_t *impulse, |
| signal_t *signal, |
| ei_impulse_result_t *result, |
| bool debug = false) |
| { |
| return process_impulse(impulse, signal, result, debug); |
| } |
|
|
| #if EI_CLASSIFIER_FREEFORM_OUTPUT |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| __attribute__((unused)) EI_IMPULSE_ERROR ei_set_freeform_output( |
| ei_impulse_handle_t *impulse_handle, |
| ei::matrix_t *freeform_outputs, |
| size_t freeform_outputs_size |
| ) { |
| |
| if (freeform_outputs_size != impulse_handle->impulse->freeform_outputs_size) { |
| EI_LOGE("ERR: freeform_outputs_size should be of size %d, but was %d. You can get the required number of freeform outputs via impulse->freeform_outputs_size.\n", |
| (int)freeform_outputs_size, (int)impulse_handle->impulse->freeform_outputs_size); |
| return EI_IMPULSE_FREEFORM_OUTPUT_SIZE_MISMATCH; |
| } |
|
|
| |
| for (size_t ix = 0; ix < freeform_outputs_size; ix++) { |
| matrix_t& freeform_output = freeform_outputs[ix]; |
| if (freeform_output.rows * freeform_output.cols != impulse_handle->impulse->freeform_outputs[ix]) { |
| EI_LOGE("ERR: freeform_outputs at index %d has the wrong size. Expected %d elements, but freeform_output is %d elements. You can get the required size via impulse->freeform_outputs[%d].\n", |
| (int)ix, |
| (int)impulse_handle->impulse->freeform_outputs[ix], |
| (int)freeform_output.rows * freeform_output.cols, |
| (int)ix); |
| return EI_IMPULSE_FREEFORM_OUTPUT_SIZE_MISMATCH; |
| } |
| } |
|
|
| impulse_handle->freeform_outputs = freeform_outputs; |
|
|
| return EI_IMPULSE_OK; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| extern "C" EI_IMPULSE_ERROR ei_set_freeform_output( |
| ei::matrix_t *freeform_outputs, |
| size_t freeform_outputs_size |
| ) { |
| return ei_set_freeform_output(&ei_default_impulse, freeform_outputs, freeform_outputs_size); |
| } |
| #endif |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #if EI_CLASSIFIER_INFERENCING_ENGINE == EI_CLASSIFIER_VLM_CONNECTOR |
| __attribute__((unused)) EI_IMPULSE_ERROR ei_get_image_input_params( |
| ei_impulse_handle_t *handle, |
| uint32_t *width, |
| uint32_t *height, |
| uint8_t *channels |
| ) { |
| const ei_impulse_t *impulse = handle->impulse; |
| if (handle->input_params == nullptr) { |
| *width = impulse->input_width; |
| *height = impulse->input_height; |
| *channels = impulse->nn_input_frame_size / (impulse->input_width * impulse->input_height); |
| } |
| else { |
| *width = handle->input_params->input_width; |
| *height = handle->input_params->input_height; |
| *channels = handle->input_params->nn_input_frame_size / (handle->input_params->input_width * handle->input_params->input_height); |
| } |
|
|
| return EI_IMPULSE_OK; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| __attribute__((unused)) EI_IMPULSE_ERROR ei_set_image_input_params( |
| ei_impulse_handle_t *handle, |
| uint32_t width, |
| uint32_t height, |
| uint8_t channels |
| ) { |
| std::unique_ptr<ei_input_params> params(new ei_input_params()); |
| if (params == nullptr) { |
| return EI_IMPULSE_OUT_OF_MEMORY; |
| } |
| params->nn_input_frame_size = width * height * channels; |
| params->raw_sample_count = width * height; |
| params->raw_samples_per_frame = width * height; |
| params->dsp_input_frame_size = width * height; |
| params->input_width = width; |
| params->input_height = height; |
| params->input_frames = 1; |
| params->interval_ms = 0.0f; |
| params->frequency = 0.0f; |
|
|
| handle->input_params = params.release(); |
| return EI_IMPULSE_OK; |
| } |
| #endif |
|
|
| |
|
|
| |
|
|
| |
| |
|
|
| #if EIDSP_SIGNAL_C_FN_POINTER == 0 |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| __attribute__((unused)) EI_IMPULSE_ERROR run_impulse( |
| #if (defined(EI_CLASSIFIER_HAS_SAMPLER) && EI_CLASSIFIER_HAS_SAMPLER == 1) || defined(__DOXYGEN__) |
| EdgeSampler *sampler, |
| #endif |
| ei_impulse_result_t *result, |
| #ifdef __MBED__ |
| mbed::Callback<void(float*, size_t)> data_fn, |
| #else |
| std::function<void(float*, size_t)> data_fn, |
| #endif |
| bool debug = false) { |
|
|
| auto& impulse = *(ei_default_impulse.impulse); |
|
|
| float *x = (float*)calloc(impulse.dsp_input_frame_size, sizeof(float)); |
| if (!x) { |
| return EI_IMPULSE_OUT_OF_MEMORY; |
| } |
|
|
| uint64_t next_tick = 0; |
|
|
| uint64_t sampling_us_start = ei_read_timer_us(); |
|
|
| |
| for (int i = 0; i < (int)impulse.dsp_input_frame_size; i += impulse.raw_samples_per_frame) { |
| uint64_t curr_us = ei_read_timer_us() - sampling_us_start; |
|
|
| next_tick = curr_us + (impulse.interval_ms * 1000); |
|
|
| data_fn(x + i, impulse.raw_samples_per_frame); |
| #if defined(EI_CLASSIFIER_HAS_SAMPLER) && EI_CLASSIFIER_HAS_SAMPLER == 1 |
| if (sampler != NULL) { |
| sampler->write_sensor_data(x + i, impulse.raw_samples_per_frame); |
| } |
| #endif |
|
|
| if (ei_run_impulse_check_canceled() == EI_IMPULSE_CANCELED) { |
| free(x); |
| return EI_IMPULSE_CANCELED; |
| } |
|
|
| while (next_tick > ei_read_timer_us() - sampling_us_start); |
| } |
|
|
| result->timing.sampling = (ei_read_timer_us() - sampling_us_start) / 1000; |
|
|
| signal_t signal; |
| int err = numpy::signal_from_buffer(x, impulse.dsp_input_frame_size, &signal); |
| if (err != 0) { |
| free(x); |
| ei_printf("ERR: signal_from_buffer failed (%d)\n", err); |
| return EI_IMPULSE_DSP_ERROR; |
| } |
|
|
| EI_IMPULSE_ERROR r = run_classifier(&signal, result, debug); |
| free(x); |
| return r; |
| } |
|
|
| #if (defined(EI_CLASSIFIER_HAS_SAMPLER) && EI_CLASSIFIER_HAS_SAMPLER == 1) || defined(__DOXYGEN__) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| __attribute__((unused)) EI_IMPULSE_ERROR run_impulse( |
| ei_impulse_result_t *result, |
| #ifdef __MBED__ |
| mbed::Callback<void(float*, size_t)> data_fn, |
| #else |
| std::function<void(float*, size_t)> data_fn, |
| #endif |
| bool debug = false) { |
| return run_impulse(NULL, result, data_fn, debug); |
| } |
| #endif |
|
|
| #endif |
|
|
| #ifdef __cplusplus |
| } |
| #endif |
|
|
| #endif |
|
|