diff --git a/EdgeImpulse.EI-SDK.pdsc b/EdgeImpulse.EI-SDK.pdsc
index 825849b..86c5777 100644
--- a/EdgeImpulse.EI-SDK.pdsc
+++ b/EdgeImpulse.EI-SDK.pdsc
@@ -5,13 +5,16 @@
EI-SDK
LICENSE-apache-2.0.txt
Edge Impulse SDK
- https://github.com/edgeimpulse/edge-impulse-sdk-pack/releases/download/v1.53.7/
+ https://github.com/edgeimpulse/edge-impulse-sdk-pack/releases/download/v1.53.12/
hello@edgeimpulse.com
https://github.com/edgeimpulse/edge-impulse-sdk-pack.git
-
+
EI-SDK
+
+ EI-SDK
+
EI-SDK
@@ -98,9 +101,6 @@
EI-SDK
-
-
- EI-SDK
@@ -146,7 +146,7 @@
-
+
Edge Impulse SDK
diff --git a/EdgeImpulse.pidx b/EdgeImpulse.pidx
index ca0eb11..9203f30 100644
--- a/EdgeImpulse.pidx
+++ b/EdgeImpulse.pidx
@@ -2,8 +2,8 @@
EdgeImpulse
https://raw.githubusercontent.com/edgeimpulse/edge-impulse-sdk-pack/main/
- 2024-07-05 09:15:15
+ 2024-07-10 16:14:04
-
+
diff --git a/edgeimpulse/edge-impulse-sdk/classifier/ei_classifier_types.h b/edgeimpulse/edge-impulse-sdk/classifier/ei_classifier_types.h
index b9404ea..b61141d 100644
--- a/edgeimpulse/edge-impulse-sdk/classifier/ei_classifier_types.h
+++ b/edgeimpulse/edge-impulse-sdk/classifier/ei_classifier_types.h
@@ -28,32 +28,32 @@
/**
* @defgroup ei_structs Structs
- *
+ *
* Public-facing structs for Edge Impulse C++ SDK.
- *
+ *
* @addtogroup ei_structs
* @{
*/
/**
* @brief Holds the output of inference, anomaly results, and timing information.
- *
+ *
* `ei_impulse_result_t` holds the output of `run_classifier()`. If object detection is
* enabled, then the output results is a
* pointer to an array of bounding boxes of size `bounding_boxes_count`, as given by
* [ei_impulse_result_bounding_box_t](https://docs.edgeimpulse.com/reference/ei_impulse_result_bounding_box_t).
* Otherwise, results are stored as an array of classification scores, as given by
* [ei_impulse_result_classification_t](https://docs.edgeimpulse.com/reference/ei_impulse_result_classification_t).
- *
+ *
* If anomaly detection is enabled (e.g. `EI_CLASSIFIER_HAS_ANOMALY == 1`), then the
* anomaly score will be stored as a floating point value in `anomaly`.
- *
- * Timing information is stored in an
+ *
+ * Timing information is stored in an
* [ei_impulse_result_timing_t](https://docs.edgeimpulse.com/reference/ei_impulse_result_timing_t)
* struct.
- *
+ *
* **Source**: [classifier/ei_classifier_types.h](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/classifier/ei_classifier_types.h)
- *
+ *
* **Example**: [standalone inferencing main.cpp](https://github.com/edgeimpulse/example-standalone-inferencing/blob/master/source/main.cpp)
*/
typedef struct {
@@ -70,16 +70,16 @@ typedef struct {
/**
* @brief Holds the output of visual anomaly detection (FOMO-AD)
- *
+ *
* If visual anomaly detection is enabled (e.g. `EI_CLASSIFIER_HAS_VISUAL_ANOMALY ==
- * 1`), then the output results will be a pointer to an array of grid cells of size
- * `visual_ad_count`, as given by
+ * 1`), then the output results will be a pointer to an array of grid cells of size
+ * `visual_ad_count`, as given by
* [ei_impulse_result_bounding_box_t](https://docs.edgeimpulse.com/reference/ei_impulse_result_bounding_box_t).
- *
+ *
* The visual anomaly detection result is stored in `visual_ad_result`, which contains the mean and max values of the grid cells.
- *
+ *
* **Source**: [classifier/ei_classifier_types.h](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/classifier/ei_classifier_types.h)
- *
+ *
* **Example**: [standalone inferencing main.cpp](https://github.com/edgeimpulse/example-standalone-inferencing/blob/master/source/main.cpp)
*/
typedef struct {
@@ -96,7 +96,7 @@ typedef struct {
/**
* @brief Holds information for a single bounding box.
- *
+ *
* If object detection is enabled (i.e. `EI_CLASSIFIER_OBJECT_DETECTION == 1`), then
* inference results will be one or more bounding boxes. The bounding boxes with the
* highest confidence scores (assuming those scores are equal to or greater than
@@ -105,20 +105,20 @@ typedef struct {
* least `EI_CLASSIFIER_OBJECT_DETECTION_COUNT`. The exact number of bounding boxes
* is stored in `bounding_boxes_count` field of [ei_impulse_result_t]/C++ Inference
* SDK Library/structs/ei_impulse_result_t.md).
- *
- * A bounding box is a rectangle that ideally surrounds the identified object. The
+ *
+ * A bounding box is a rectangle that ideally surrounds the identified object. The
* (`x`, `y`) coordinates in the struct identify the top-left corner of the box.
* `label` is the predicted class with the highest confidence score. `value` is the
* confidence score between [0.0..1.0] of the given `label`.
- *
+ *
* **Source**: [classifier/ei_classifier_types.h](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/classifier/ei_classifier_types.h)
- *
+ *
* **Example**: [standalone inferencing main.cpp](https://github.com/edgeimpulse/example-standalone-inferencing/blob/master/source/main.cpp)
*/
typedef struct {
/**
- * Pointer to a character array describing the associated class of the given
- * bounding box. Taken from one of the elements of
+ * Pointer to a character array describing the associated class of the given
+ * bounding box. Taken from one of the elements of
* `ei_classifier_inferencing_categories[]`.
*/
const char *label;
@@ -151,19 +151,19 @@ typedef struct {
/**
* @brief Holds timing information about the processing (DSP) and inference blocks.
- *
+ *
* Records timing information during the execution of the preprocessing (DSP) and
* inference blocks. Can be used to determine if inference will meet timing requirements
* on your particular platform.
- *
+ *
* **Source**: [classifier/ei_classifier_types.h](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/classifier/ei_classifier_types.h)
- *
+ *
* **Example**: [standalone inferencing main.cpp](https://github.com/edgeimpulse/example-standalone-inferencing/blob/master/source/main.cpp)
*/
typedef struct {
/**
* If using `run_impulse()` to perform sampling and inference, it is the amount of
- * time (in milliseconds) it took to fetch raw samples. Not used for
+ * time (in milliseconds) it took to fetch raw samples. Not used for
* `run_classifier()`.
*/
int sampling;
@@ -203,23 +203,23 @@ typedef struct {
/**
* @brief Holds the output of inference, anomaly results, and timing information.
- *
+ *
* `ei_impulse_result_t` holds the output of `run_classifier()`. If object detection is
* enabled (e.g. `EI_CLASSIFIER_OBJECT_DETECTION == 1`), then the output results is a
* pointer to an array of bounding boxes of size `bounding_boxes_count`, as given by
- * [ei_impulse_result_bounding_box_t](https://docs.edgeimpulse.com/reference/ei_impulse_result_bounding_box_t).
+ * [ei_impulse_result_bounding_box_t](https://docs.edgeimpulse.com/reference/ei_impulse_result_bounding_box_t).
* Otherwise, results are stored as an array of classification scores, as given by
* [ei_impulse_result_classification_t](https://docs.edgeimpulse.com/reference/ei_impulse_result_classification_t).
- *
+ *
* If anomaly detection is enabled (e.g. `EI_CLASSIFIER_HAS_ANOMALY == 1`), then the
* anomaly score will be stored as a floating point value in `anomaly`.
- *
- * Timing information is stored in an
- * [ei_impulse_result_timing_t](https://docs.edgeimpulse.com/reference/ei_impulse_result_timing_t)
+ *
+ * Timing information is stored in an
+ * [ei_impulse_result_timing_t](https://docs.edgeimpulse.com/reference/ei_impulse_result_timing_t)
* struct.
- *
+ *
* **Source**: [classifier/ei_classifier_types.h](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/classifier/ei_classifier_types.h)
- *
+ *
* **Example**: [standalone inferencing main.cpp](https://github.com/edgeimpulse/example-standalone-inferencing/blob/master/source/main.cpp)
*/
typedef struct {
@@ -238,13 +238,18 @@ typedef struct {
* Array of classification results. If object detection is enabled, this will be
* empty.
*/
+#ifdef EI_DSP_RESULT_OVERRIDE
+ // For CI only. We will create the array to hold results
+ ei_impulse_result_classification_t* classification;
+#else
#if EI_CLASSIFIER_LABEL_COUNT == 0
// EI_CLASSIFIER_LABEL_COUNT can be 0 for anomaly only models
// to prevent compiler warnings/errors, we need to have at least one element
ei_impulse_result_classification_t classification[1];
#else
ei_impulse_result_classification_t classification[EI_CLASSIFIER_LABEL_COUNT];
-#endif
+#endif // EI_CLASSIFIER_LABEL_COUNT == 0
+#endif // EI_DSP_RESULT_OVERRIDE else
/**
* Anomaly score. If anomaly detection is not enabled, this will be 0. A higher
diff --git a/edgeimpulse/edge-impulse-sdk/classifier/ei_fill_result_struct.h b/edgeimpulse/edge-impulse-sdk/classifier/ei_fill_result_struct.h
index b6f2b32..e3bfd04 100644
--- a/edgeimpulse/edge-impulse-sdk/classifier/ei_fill_result_struct.h
+++ b/edgeimpulse/edge-impulse-sdk/classifier/ei_fill_result_struct.h
@@ -374,7 +374,13 @@ __attribute__((unused)) static EI_IMPULSE_ERROR fill_result_struct_f32(const ei_
ei_impulse_result_t *result,
float *data,
bool debug) {
- for (uint32_t ix = 0; ix < impulse->label_count; ix++) {
+#ifdef EI_DSP_RESULT_OVERRIDE
+ uint32_t stop_count = EI_DSP_RESULT_OVERRIDE;
+#else
+ uint32_t stop_count = impulse->label_count;
+#endif
+ for (uint32_t ix = 0; ix < stop_count; ix++) {
+
float value = data[ix];
if (debug) {
@@ -382,7 +388,10 @@ __attribute__((unused)) static EI_IMPULSE_ERROR fill_result_struct_f32(const ei_
ei_printf_float(value);
ei_printf("\n");
}
+// For testing purposes, we will have more values than labels
+#ifndef EI_DSP_RESULT_OVERRIDE
result->classification[ix].label = impulse->categories[ix];
+#endif
result->classification[ix].value = value;
}
diff --git a/edgeimpulse/edge-impulse-sdk/classifier/ei_run_classifier.h b/edgeimpulse/edge-impulse-sdk/classifier/ei_run_classifier.h
index 02a3523..d9b0e6f 100644
--- a/edgeimpulse/edge-impulse-sdk/classifier/ei_run_classifier.h
+++ b/edgeimpulse/edge-impulse-sdk/classifier/ei_run_classifier.h
@@ -237,7 +237,10 @@ extern "C" EI_IMPULSE_ERROR process_impulse(ei_impulse_handle_t *handle,
}
#endif
+#ifndef EI_DSP_RESULT_OVERRIDE
+ // Don't wipe in CI, as we store a pointer
memset(result, 0, sizeof(ei_impulse_result_t));
+#endif
uint32_t block_num = handle->impulse->dsp_blocks_size + handle->impulse->learning_blocks_size;
// smart pointer to features array
@@ -745,25 +748,25 @@ will be documented by Doxygen. */
/**
* @defgroup ei_functions Functions
- *
- * Public-facing functions for running inference using the Edge Impulse C++ library.
- *
+ *
+ * Public-facing functions for running inference using the Edge Impulse C++ library.
+ *
* **Source**: [classifier/ei_run_classifier.h](https://github.com/edgeimpulse/inferencing-sdk-cpp/blob/master/classifier/ei_run_classifier.h)
- *
+ *
* @addtogroup ei_functions
* @{
*/
/**
- * @brief Initialize static variables for running preprocessing and inference
+ * @brief Initialize static variables for running preprocessing and inference
* continuously.
- *
+ *
* Initializes and clears any internal static variables needed by `run_classifier_continuous()`.
* This includes the moving average filter (MAF). This function should be called prior to
* calling `run_classifier_continuous()`.
- *
+ *
* **Blocking**: yes
- *
+ *
* **Example**: [nano_ble33_sense_microphone_continuous.ino](https://github.com/edgeimpulse/example-lacuna-ls200/blob/main/nano_ble33_sense_microphone_continous/nano_ble33_sense_microphone_continuous.ino)
*/
extern "C" void run_classifier_init(void)
@@ -786,17 +789,17 @@ extern "C" void run_classifier_init(void)
}
/**
- * @brief Initialize static variables for running preprocessing and inference
+ * @brief Initialize static variables for running preprocessing and inference
* continuously.
- *
+ *
* Initializes and clears any internal static variables needed by `run_classifier_continuous()`.
* This includes the moving average filter (MAF). This function should be called prior to
* calling `run_classifier_continuous()`.
- *
+ *
* **Blocking**: yes
- *
+ *
* **Example**: [nano_ble33_sense_microphone_continuous.ino](https://github.com/edgeimpulse/example-lacuna-ls200/blob/main/nano_ble33_sense_microphone_continous/nano_ble33_sense_microphone_continuous.ino)
- *
+ *
* @param[in] handle struct with information about model and DSP
*/
__attribute__((unused)) void run_classifier_init(ei_impulse_handle_t *handle)
@@ -818,13 +821,13 @@ __attribute__((unused)) void run_classifier_init(ei_impulse_handle_t *handle)
/**
* @brief Deletes static variables when running preprocessing and inference continuously.
- *
+ *
* Deletes internal static variables used by `run_classifier_continuous()`, which
* includes the moving average filter (MAF). This function should be called when you
* are done running continuous classification.
- *
+ *
* **Blocking**: yes
- *
+ *
* **Example**: [ei_run_audio_impulse.cpp](https://github.com/edgeimpulse/firmware-nordic-thingy53/blob/main/src/inference/ei_run_audio_impulse.cpp)
*/
extern "C" void run_classifier_deinit(void)
@@ -835,18 +838,18 @@ extern "C" void run_classifier_deinit(void)
}
/**
- * @brief Run preprocessing (DSP) on new slice of raw features. Add output features
+ * @brief Run preprocessing (DSP) on new slice of raw features. Add output features
* to rolling matrix and run inference on full sample.
*
- * Accepts a new slice of features give by the callback defined in the `signal` parameter.
- * It performs preprocessing (DSP) on this new slice of features and appends the output to
+ * Accepts a new slice of features give by the callback defined in the `signal` parameter.
+ * It performs preprocessing (DSP) on this new slice of features and appends the output to
* a sliding window of pre-processed features (stored in a static features matrix). The matrix
- * stores the new slice and as many old slices as necessary to make up one full sample for
+ * stores the new slice and as many old slices as necessary to make up one full sample for
* performing inference.
- *
- * `run_classifier_init()` must be called before making any calls to
+ *
+ * `run_classifier_init()` must be called before making any calls to
* `run_classifier_continuous().`
- *
+ *
* For example, if you are doing keyword spotting on 1-second slices of audio and you want to
* perform inference 4 times per second (given by `EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW`), you
* would collect 0.25 seconds of audio and call run_classifier_continuous(). The function would
@@ -854,33 +857,33 @@ extern "C" void run_classifier_deinit(void)
* drop the oldest 0.25 seconds' worth of MFCCs from its internal matrix, and append the newest
* slice of MFCCs. This process allows the library to keep track of the pre-processed features
* (e.g. MFCCs) in the window instead of the entire set of raw features (e.g. raw audio data),
- * which can potentially save a lot of space in RAM. After updating the static matrix,
- * inference is performed using the whole matrix, which acts as a sliding window of
+ * which can potentially save a lot of space in RAM. After updating the static matrix,
+ * inference is performed using the whole matrix, which acts as a sliding window of
* pre-processed features.
- *
- * Additionally, a moving average filter (MAF) can be enabled for `run_classifier_continuous()`,
- * which averages (arithmetic mean) the last *n* inference results for each class. *n* is
- * `EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW / 2`. In our example above, if we enabled the MAF, the
+ *
+ * Additionally, a moving average filter (MAF) can be enabled for `run_classifier_continuous()`,
+ * which averages (arithmetic mean) the last *n* inference results for each class. *n* is
+ * `EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW / 2`. In our example above, if we enabled the MAF, the
* values in `result` would contain predictions averaged from the previous 2 inferences.
- *
- * To learn more about `run_classifier_continuous()`, see
- * [this guide](https://docs.edgeimpulse.com/docs/tutorials/advanced-inferencing/continuous-audio-sampling)
+ *
+ * To learn more about `run_classifier_continuous()`, see
+ * [this guide](https://docs.edgeimpulse.com/docs/tutorials/advanced-inferencing/continuous-audio-sampling)
* on continuous audio sampling. While the guide is written for audio signals, the concepts of continuous sampling and inference can be extrapolated to any time-series data.
- *
+ *
* **Blocking**: yes
- *
+ *
* **Example**: [nano_ble33_sense_microphone_continuous.ino](https://github.com/edgeimpulse/example-lacuna-ls200/blob/main/nano_ble33_sense_microphone_continous/nano_ble33_sense_microphone_continuous.ino)
- *
- * @param[in] signal Pointer to a signal_t struct that contains the number of elements in the
- * slice of raw features (e.g. `EI_CLASSIFIER_SLICE_SIZE`) and a pointer to a callback that reads
+ *
+ * @param[in] signal Pointer to a signal_t struct that contains the number of elements in the
+ * slice of raw features (e.g. `EI_CLASSIFIER_SLICE_SIZE`) and a pointer to a callback that reads
* in the slice of raw features.
- * @param[out] result Pointer to an `ei_impulse_result_t` struct that contains the various output
+ * @param[out] result Pointer to an `ei_impulse_result_t` struct that contains the various output
* results from inference after run_classifier() returns.
- * @param[in] debug Print internal preprocessing and inference debugging information via
+ * @param[in] debug Print internal preprocessing and inference debugging information via
* `ei_printf()`.
* @param[in] enable_maf Enable the moving average filter (MAF) for the classifier.
*
- * @return Error code as defined by `EI_IMPULSE_ERROR` enum. Will be `EI_IMPULSE_OK` if inference
+ * @return Error code as defined by `EI_IMPULSE_ERROR` enum. Will be `EI_IMPULSE_OK` if inference
* completed successfully.
*/
extern "C" EI_IMPULSE_ERROR run_classifier_continuous(
@@ -894,18 +897,18 @@ extern "C" EI_IMPULSE_ERROR run_classifier_continuous(
}
/**
- * @brief Run preprocessing (DSP) on new slice of raw features. Add output features
+ * @brief Run preprocessing (DSP) on new slice of raw features. Add output features
* to rolling matrix and run inference on full sample.
*
- * Accepts a new slice of features give by the callback defined in the `signal` parameter.
- * It performs preprocessing (DSP) on this new slice of features and appends the output to
+ * Accepts a new slice of features give by the callback defined in the `signal` parameter.
+ * It performs preprocessing (DSP) on this new slice of features and appends the output to
* a sliding window of pre-processed features (stored in a static features matrix). The matrix
- * stores the new slice and as many old slices as necessary to make up one full sample for
+ * stores the new slice and as many old slices as necessary to make up one full sample for
* performing inference.
- *
- * `run_classifier_init()` must be called before making any calls to
+ *
+ * `run_classifier_init()` must be called before making any calls to
* `run_classifier_continuous().`
- *
+ *
* For example, if you are doing keyword spotting on 1-second slices of audio and you want to
* perform inference 4 times per second (given by `EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW`), you
* would collect 0.25 seconds of audio and call run_classifier_continuous(). The function would
@@ -913,34 +916,34 @@ extern "C" EI_IMPULSE_ERROR run_classifier_continuous(
* drop the oldest 0.25 seconds' worth of MFCCs from its internal matrix, and append the newest
* slice of MFCCs. This process allows the library to keep track of the pre-processed features
* (e.g. MFCCs) in the window instead of the entire set of raw features (e.g. raw audio data),
- * which can potentially save a lot of space in RAM. After updating the static matrix,
- * inference is performed using the whole matrix, which acts as a sliding window of
+ * which can potentially save a lot of space in RAM. After updating the static matrix,
+ * inference is performed using the whole matrix, which acts as a sliding window of
* pre-processed features.
- *
- * Additionally, a moving average filter (MAF) can be enabled for `run_classifier_continuous()`,
- * which averages (arithmetic mean) the last *n* inference results for each class. *n* is
- * `EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW / 2`. In our example above, if we enabled the MAF, the
+ *
+ * Additionally, a moving average filter (MAF) can be enabled for `run_classifier_continuous()`,
+ * which averages (arithmetic mean) the last *n* inference results for each class. *n* is
+ * `EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW / 2`. In our example above, if we enabled the MAF, the
* values in `result` would contain predictions averaged from the previous 2 inferences.
- *
- * To learn more about `run_classifier_continuous()`, see
- * [this guide](https://docs.edgeimpulse.com/docs/tutorials/advanced-inferencing/continuous-audio-sampling)
+ *
+ * To learn more about `run_classifier_continuous()`, see
+ * [this guide](https://docs.edgeimpulse.com/docs/tutorials/advanced-inferencing/continuous-audio-sampling)
* on continuous audio sampling. While the guide is written for audio signals, the concepts of continuous sampling and inference can be extrapolated to any time-series data.
- *
+ *
* **Blocking**: yes
- *
+ *
* **Example**: [nano_ble33_sense_microphone_continuous.ino](https://github.com/edgeimpulse/example-lacuna-ls200/blob/main/nano_ble33_sense_microphone_continous/nano_ble33_sense_microphone_continuous.ino)
- *
+ *
* @param[in] impulse `ei_impulse_handle_t` struct with information about preprocessing and model.
- * @param[in] signal Pointer to a signal_t struct that contains the number of elements in the
- * slice of raw features (e.g. `EI_CLASSIFIER_SLICE_SIZE`) and a pointer to a callback that reads
+ * @param[in] signal Pointer to a signal_t struct that contains the number of elements in the
+ * slice of raw features (e.g. `EI_CLASSIFIER_SLICE_SIZE`) and a pointer to a callback that reads
* in the slice of raw features.
- * @param[out] result Pointer to an `ei_impulse_result_t` struct that contains the various output
+ * @param[out] result Pointer to an `ei_impulse_result_t` struct that contains the various output
* results from inference after run_classifier() returns.
- * @param[in] debug Print internal preprocessing and inference debugging information via
+ * @param[in] debug Print internal preprocessing and inference debugging information via
* `ei_printf()`.
* @param[in] enable_maf Enable the moving average filter (MAF) for the classifier.
*
- * @return Error code as defined by `EI_IMPULSE_ERROR` enum. Will be `EI_IMPULSE_OK` if inference
+ * @return Error code as defined by `EI_IMPULSE_ERROR` enum. Will be `EI_IMPULSE_OK` if inference
* completed successfully.
*/
__attribute__((unused)) EI_IMPULSE_ERROR run_classifier_continuous(
@@ -955,19 +958,19 @@ __attribute__((unused)) EI_IMPULSE_ERROR run_classifier_continuous(
/**
* @brief Run the classifier over a raw features array.
- *
- *
+ *
+ *
* Overloaded function [run_classifier()](#run_classifier-1) that defaults to the single impulse.
- *
+ *
* **Blocking**: yes
- *
- * @param[in] signal Pointer to a `signal_t` struct that contains the total length of the raw
+ *
+ * @param[in] signal Pointer to a `signal_t` struct that contains the total length of the raw
* feature array, which must match EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, and a pointer to a callback
* that reads in the raw features.
- * @param[out] result Pointer to an ei_impulse_result_t struct that will contain the various output
+ * @param[out] result Pointer to an ei_impulse_result_t struct that will contain the various output
* results from inference after `run_classifier()` returns.
* @param[in] debug Print internal preprocessing and inference debugging information via `ei_printf()`.
- *
+ *
* @return Error code as defined by `EI_IMPULSE_ERROR` enum. Will be `EI_IMPULSE_OK` if inference
* completed successfully.
*/
@@ -981,26 +984,26 @@ extern "C" EI_IMPULSE_ERROR run_classifier(
/**
* @brief Run the classifier over a raw features array.
- *
- *
- * Accepts a `signal_t` input struct pointing to a callback that reads in pages of raw features.
- * `run_classifier()` performs any necessary preprocessing on the raw features (e.g. DSP, cropping
- * of images, etc.) before performing inference. Results from inference are stored in an
+ *
+ *
+ * Accepts a `signal_t` input struct pointing to a callback that reads in pages of raw features.
+ * `run_classifier()` performs any necessary preprocessing on the raw features (e.g. DSP, cropping
+ * of images, etc.) before performing inference. Results from inference are stored in an
* `ei_impulse_result_t` struct.
- *
+ *
* **Blocking**: yes
- *
+ *
* **Example**: [standalone inferencing main.cpp](https://github.com/edgeimpulse/example-standalone-inferencing/blob/master/source/main.cpp)
- *
+ *
* @param[in] impulse Pointer to an `ei_impulse_handle_t` struct that contains the model and
* preprocessing information.
- * @param[in] signal Pointer to a `signal_t` struct that contains the total length of the raw
+ * @param[in] signal Pointer to a `signal_t` struct that contains the total length of the raw
* feature array, which must match EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, and a pointer to a callback
* that reads in the raw features.
- * @param[out] result Pointer to an ei_impulse_result_t struct that will contain the various output
+ * @param[out] result Pointer to an ei_impulse_result_t struct that will contain the various output
* results from inference after `run_classifier()` returns.
* @param[in] debug Print internal preprocessing and inference debugging information via `ei_printf()`.
- *
+ *
* @return Error code as defined by `EI_IMPULSE_ERROR` enum. Will be `EI_IMPULSE_OK` if inference
* completed successfully.
*/
@@ -1023,17 +1026,17 @@ Do not use these - if possible, change your code to reflect the upcoming changes
#if EIDSP_SIGNAL_C_FN_POINTER == 0
/**
- * @brief Run the impulse, if you provide an instance of sampler it will also persist
+ * @brief Run the impulse, if you provide an instance of sampler it will also persist
* the data for you.
- *
- * @deprecated This function is deprecated and will be removed in future versions. Use
+ *
+ * @deprecated This function is deprecated and will be removed in future versions. Use
* `run_classifier()` instead.
- *
+ *
* @param[in] sampler Instance to an **initialized** sampler
* @param[out] result Object to store the results in
* @param[in] data_fn Callback function to retrieve data from sensors
* @param[in] debug Whether to log debug messages (default false)
- *
+ *
* @return Error code as defined by `EI_IMPULSE_ERROR` enum. Will be `EI_IMPULSE_OK` if inference
* completed successfully.
*/
@@ -1099,14 +1102,14 @@ __attribute__((unused)) EI_IMPULSE_ERROR run_impulse(
#if (defined(EI_CLASSIFIER_HAS_SAMPLER) && EI_CLASSIFIER_HAS_SAMPLER == 1) || defined(__DOXYGEN__)
/**
* @brief Run the impulse, does not persist data.
- *
- * @deprecated This function is deprecated and will be removed in future versions. Use
+ *
+ * @deprecated This function is deprecated and will be removed in future versions. Use
* `run_classifier()` instead.
- *
+ *
* @param[out] result Object to store the results in
* @param[in] data_fn Callback function to retrieve data from sensors
* @param[out] debug Whether to log debug messages (default false)
- *
+ *
* @return Error code as defined by `EI_IMPULSE_ERROR` enum. Will be `EI_IMPULSE_OK` if inference
* completed successfully.
*/
diff --git a/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/anomaly.h b/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/anomaly.h
index 5a800eb..7e595bf 100644
--- a/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/anomaly.h
+++ b/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/anomaly.h
@@ -163,7 +163,7 @@ EI_IMPULSE_ERROR run_kmeans_anomaly(
{
ei_learning_block_config_anomaly_kmeans_t *block_config = (ei_learning_block_config_anomaly_kmeans_t*)config_ptr;
- uint64_t anomaly_start_ms = ei_read_timer_ms();
+ uint64_t anomaly_start_us = ei_read_timer_us();
float *input = (float*)ei_malloc(block_config->anom_axes_size * sizeof(float));
if (!input) {
@@ -177,15 +177,16 @@ EI_IMPULSE_ERROR run_kmeans_anomaly(
float anomaly = get_min_distance_to_cluster(
input, block_config->anom_axes_size, block_config->anom_clusters, block_config->anom_cluster_count);
- uint64_t anomaly_end_ms = ei_read_timer_ms();
+ uint64_t anomaly_end_us = ei_read_timer_us();
if (debug) {
- ei_printf("Anomaly score (time: %d ms.): ", static_cast(anomaly_end_ms - anomaly_start_ms));
+ ei_printf("Anomaly score (time: %d ms.): ", static_cast(anomaly_end_us - anomaly_start_us));
ei_printf_float(anomaly);
ei_printf("\n");
}
- result->timing.anomaly = anomaly_end_ms - anomaly_start_ms;
+ result->timing.anomaly_us = anomaly_end_us - anomaly_start_us;
+ result->timing.anomaly = (int)(result->timing.anomaly_us/1000);
result->anomaly = anomaly;
ei_free(input);
@@ -258,6 +259,7 @@ EI_IMPULSE_ERROR run_gmm_anomaly(
ei_printf("\n");
}
+ result->timing.anomaly_us = anomaly_result.timing.classification_us;
result->timing.anomaly = anomaly_result.timing.classification;
if (block_config->classification_mode == EI_CLASSIFIER_CLASSIFICATION_MODE_VISUAL_ANOMALY) {
diff --git a/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/tflite_eon.h b/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/tflite_eon.h
index c1053e0..2eacaa9 100644
--- a/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/tflite_eon.h
+++ b/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/tflite_eon.h
@@ -261,8 +261,6 @@ EI_IMPULSE_ERROR run_nn_inference(
graph_config->model_reset(ei_aligned_free);
- result->timing.classification_us = ei_read_timer_us() - ctx_start_us;
-
if (run_res != EI_IMPULSE_OK) {
return run_res;
}
@@ -361,8 +359,6 @@ EI_IMPULSE_ERROR run_nn_inference_image_quantized(
return run_res;
}
- result->timing.classification_us = ei_read_timer_us() - ctx_start_us;
-
return EI_IMPULSE_OK;
}
#endif // EI_CLASSIFIER_QUANTIZATION_ENABLED == 1
diff --git a/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/tflite_micro.h b/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/tflite_micro.h
index fd3cb26..4710b74 100644
--- a/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/tflite_micro.h
+++ b/edgeimpulse/edge-impulse-sdk/classifier/inferencing_engines/tflite_micro.h
@@ -331,8 +331,6 @@ EI_IMPULSE_ERROR run_nn_inference(
}
}
- result->timing.classification_us = ei_read_timer_us() - ctx_start_us;
-
if (run_res != EI_IMPULSE_OK) {
return run_res;
}
@@ -427,8 +425,6 @@ EI_IMPULSE_ERROR run_nn_inference_image_quantized(
return run_res;
}
- result->timing.classification_us = ei_read_timer_us() - ctx_start_us;
-
return EI_IMPULSE_OK;
}
#endif // EI_CLASSIFIER_QUANTIZATION_ENABLED == 1
diff --git a/edgeimpulse/edge-impulse-sdk/dsp/ei_hr.hpp b/edgeimpulse/edge-impulse-sdk/dsp/ei_hr.hpp
index 6570f9b..816191e 100644
--- a/edgeimpulse/edge-impulse-sdk/dsp/ei_hr.hpp
+++ b/edgeimpulse/edge-impulse-sdk/dsp/ei_hr.hpp
@@ -26,6 +26,28 @@
#include "edge-impulse-sdk/dsp/numpy.hpp"
#include "edge-impulse-sdk/dsp/ei_dsp_handle.h"
#include "edge-impulse-enterprise/hr/hr_ppg.hpp"
+#include "edge-impulse-enterprise/hr/hrv.hpp"
+#include "edge-impulse-sdk/dsp/memory.hpp"
+
+// Need a wrapper to get ei_malloc used
+// cppyy didn't like this override for some reason
+class hrv_wrap : public ei::hrv::beats_to_hrv {
+public:
+ // Boilerplate below here
+ void* operator new(size_t size) {
+ // Custom memory allocation logic here
+ return ei_malloc(size);
+ }
+
+ void operator delete(void* ptr) {
+ // Custom memory deallocation logic here
+ ei_free(ptr);
+ }
+ // end boilerplate
+
+ // Use the same constructor as the parent
+ using ei::hrv::beats_to_hrv::beats_to_hrv;
+};
class hr_class : public DspHandle {
public:
@@ -37,32 +59,71 @@ class hr_class : public DspHandle {
int extract(ei::signal_t *signal, ei::matrix_t *output_matrix, void *config_ptr, const float frequency) override {
using namespace ei;
- // Don't need just yet
- // ei_dsp_config_hr_t config = *((ei_dsp_config_hr_t*)config_ptr);
-
-
- // TODO fix for axes / accel
- size_t samples_per_inc = ppg.win_inc_samples;
- // TODO go in a loop for the full window size, once I can actually test this vs studio
- if(signal->total_length != samples_per_inc) {
- return EIDSP_BUFFER_SIZE_MISMATCH;
+ // Using reference avoids a copy
+ ei_dsp_config_hr_t& config = *((ei_dsp_config_hr_t*)config_ptr);
+ size_t floats_per_inc = ppg.win_inc_samples * ppg.axes;
+ size_t hrv_inc_samples = config.hrv_update_interval_s * frequency * ppg.axes;
+ // Greater than b/c can have multiple increments (HRs) per window
+ assert(signal->total_length >= floats_per_inc);
+
+ int out_idx = 0;
+ size_t hrv_count = 0;
+ for(size_t i = 0; i < signal->total_length; i += floats_per_inc) {
+ // TODO ask for smaller increments and bp them into place
+ // Copy into the end of the buffer
+ matrix_t temp(ppg.win_inc_samples, ppg.axes);
+ signal->get_data(i, floats_per_inc, temp.buffer);
+ auto hr = ppg.stream(&temp);
+ if(!hrv || (hrv && config.include_hr)) {
+ output_matrix->buffer[out_idx++] = hr;
+ }
+ if(hrv) {
+ auto peaks = ppg.get_peaks();
+ hrv->add_streaming_beats(peaks);
+ hrv_count += floats_per_inc;
+ if(hrv_count >= hrv_inc_samples) {
+ fvec features = hrv->get_hrv_features(0);
+ for(size_t j = 0; j < features.size(); j++) {
+ output_matrix->buffer[out_idx++] = features[j];
+ }
+ hrv_count = 0;
+ }
+ }
}
-
- // TODO ask for smaller increments and bp them into place
- // Copy into the end of the buffer
- matrix_t temp(ppg.axes, samples_per_inc);
- signal->get_data(0, samples_per_inc, temp.buffer);
-
-
- output_matrix->buffer[0] = ppg.stream(&temp);
-
- output_matrix->rows = 1;
- output_matrix->cols = 1;
return EIDSP_OK;
}
- // TODO: actually read in config: axes too!
- hr_class(float frequency) : ppg(frequency, 1, 8*50, 2*50, true) {
+ hr_class(ei_dsp_config_hr_t* config, float frequency)
+ : ppg(frequency,
+ config->axes,
+ frequency * 8, // TODO variable window
+ frequency * 2, // TODO variable overlap
+ config->filter_preset,
+ config->acc_resting_std,
+ config->sensitivity,
+ true), hrv(nullptr)
+ {
+ auto table = config->named_axes;
+ for( size_t i = 0; i < config->named_axes_size; i++ ) {
+ ppg.set_offset_table(i, table[i].axis);
+ }
+ // if not "none"
+ if(strcmp(config->hrv_features,"none") != 0) {
+ // new is overloaded to use ei_malloc
+ hrv = new hrv_wrap(
+ frequency,
+ config->hrv_features,
+ config->hrv_update_interval_s,
+ config->hrv_win_size_s,
+ 2); // TODO variable window?
+ }
+ }
+
+ ~hr_class() {
+ if(hrv) {
+ // delete is overloaded to use ei_free
+ delete hrv;
+ }
}
// Boilerplate below here
@@ -80,13 +141,12 @@ class hr_class : public DspHandle {
// end boilerplate
private:
ei::hr_ppg ppg;
+ hrv_wrap* hrv; // pointer b/c we don't always need it
};
DspHandle* hr_class::create(void* config_in, float frequency) { // NOLINT def in header is OK at EI
- // Don't need just yet
- // auto config = reinterpret_cast(config_in);
- // TODO: actually read in config
- return new hr_class(frequency);
+ auto config = reinterpret_cast(config_in);
+ return new hr_class(config, frequency);
};
/*
diff --git a/edgeimpulse/edge-impulse-sdk/dsp/spectral/signal.hpp b/edgeimpulse/edge-impulse-sdk/dsp/spectral/signal.hpp
index 37fb0e9..4827181 100644
--- a/edgeimpulse/edge-impulse-sdk/dsp/spectral/signal.hpp
+++ b/edgeimpulse/edge-impulse-sdk/dsp/spectral/signal.hpp
@@ -38,7 +38,7 @@ class signal {
public:
using fvec = ei_vector;
- static void scale(fvec &x, float a)
+ static void scale(fvec& x, float a)
{
for (size_t ix = 0; ix < x.size(); ix++) {
x[ix] *= a;
@@ -58,12 +58,12 @@ class signal {
* @param zi Initial conditions
*/
static void decimate_simple(
- const fvec &input,
- fvec &output,
+ const fvec& input,
+ fvec& output,
size_t factor,
- const fvec &b,
- const fvec &a,
- const fvec &zi)
+ const fvec& b,
+ const fvec& a,
+ const fvec& zi)
{
fvec d = zi;
scale(d, input[0]);
@@ -85,19 +85,18 @@ class signal {
}
struct sosfilt {
- const float *coeff; // 6 * num_sections coefficients
- float* zi;
+ const float* coeff; // 6 * num_sections coefficients
fvec zi_vec; // 2 * num_sections initial conditions
size_t num_sections;
- sosfilt(const float *coeff_, const float *zi_, size_t num_sections_)
+ sosfilt(const float* coeff_, const float* zi_, size_t num_sections_)
: coeff(coeff_),
- zi_vec(zi_, zi_ + (num_sections_ * 2)),
- num_sections(num_sections_)
+ zi_vec(zi_, zi_ + (num_sections_ * 2)),
+ num_sections(num_sections_)
{
}
- void update(const float *coeff_, const float *zi_)
+ void update(const float* coeff_, const float* zi_)
{
coeff = coeff_;
zi_vec.assign(zi_, zi_ + (num_sections * 2));
@@ -110,7 +109,7 @@ class signal {
* @param output Output signal. Can be the same as input for in place
* @param x_size Minimum size of input and output signal
*/
- void run(const float *input, const size_t size, float* output)
+ void run(const float* input, const size_t size, float* output)
{
assert(num_sections > 0);
@@ -130,8 +129,8 @@ class signal {
void init(float x0)
{
for (size_t sect = 0; sect < num_sections; sect++) {
- zi_vec.data()[sect * 2] *= x0;
- zi_vec.data()[sect * 2 + 1] *= x0;
+ zi_vec[sect * 2] *= x0;
+ zi_vec[sect * 2 + 1] *= x0;
}
}
};
@@ -145,12 +144,12 @@ class signal {
* @param sos Second-order section
*/
static void decimate_simple(
- const float *input,
+ const float* input,
const size_t input_size,
- float *output,
+ float* output,
const size_t output_size,
size_t factor,
- sosfilt &sos)
+ sosfilt& sos)
{
sos.init(input[0]);
@@ -176,7 +175,7 @@ class signal {
* @param a Denominator coefficients
* @param zi Initial conditions
*/
- static void lfilter(const fvec &b, const fvec &a, const fvec &x, fvec &y, fvec &d)
+ static void lfilter(const fvec& b, const fvec& a, const fvec& x, fvec& y, fvec& d)
{
/*
a[0]*y[n] = b[0] * x[n] + d[0][n-1]
@@ -205,7 +204,7 @@ class signal {
}
}
- static void iir2(const float *x, float *y, size_t n, const float *b, const float *a, float *d)
+ static void iir2(const float* x, float* y, size_t n, const float* b, const float* a, float* d)
{
/*
a[0]*y[n] = b[0] * x[n] + d[0][n-1]
@@ -236,7 +235,7 @@ class signal {
* @param y Output signal
* @param h FIR coefficients
*/
- static void upfirdn(const float * x, size_t x_size, fvec &y, int up, int down, const fvec &h)
+ static void upfirdn(const float* x, size_t x_size, fvec& y, int up, int down, const fvec& h)
{
assert(up > 0);
assert(down > 0);
@@ -296,7 +295,7 @@ class signal {
* @param output Output signal, will be moved from an internal vector sized correctly.
* @param window FIR coefficients. e.g. signal.firwin(2 * half_len + 1, f_c, window=('kaiser', 5.0))
*/
- static void resample_poly(const float* input, size_t input_size, fvec &output, int up, int down, const fvec &window)
+ static void resample_poly(const float* input, size_t input_size, fvec& output, int up, int down, const fvec& window)
{
assert(up > 0);
assert(down > 0);
@@ -323,28 +322,31 @@ class signal {
}
static void calc_decimation_ratios(
- const char *filter_type,
+ const char* filter_type,
float filter_cutoff,
float sample_rate,
- std::vector &ratios)
+ std::vector& ratios)
{
if (strcmp(filter_type, "low") == 0) {
- ratios = {1};
+ ratios = { 1 };
return;
}
- static const std::vector supported = {1000, 100, 30, 10, 3};
+ static const std::vector supported = { 1000, 100, 30, 10, 3 };
for (size_t i = 0; i < supported.size(); i++) {
const int r = supported[i];
if (sample_rate * 0.5f / r > filter_cutoff) {
if (r == 3 || r == 10) {
- ratios = {r};
- } else if (r == 30) {
- ratios = {3, 10};
- } else if (r == 100) {
- ratios = {10, 10};
- } else if (r == 1000) {
- ratios = {10, 10, 10};
+ ratios = { r };
+ }
+ else if (r == 30) {
+ ratios = { 3, 10 };
+ }
+ else if (r == 100) {
+ ratios = { 10, 10 };
+ }
+ else if (r == 1000) {
+ ratios = { 10, 10, 10 };
}
return;
}