diff --git a/.github/workflows/run_unix.yml b/.github/workflows/run_unix.yml index f0d10f9d8..6df32e14a 100644 --- a/.github/workflows/run_unix.yml +++ b/.github/workflows/run_unix.yml @@ -458,6 +458,8 @@ jobs: run: sudo -H python3 $GITHUB_WORKSPACE/python_package/examples/tests/eeg_metrics.py --board-id -1 - name: EEG Metrics Custom Python run: sudo -H python3 $GITHUB_WORKSPACE/python_package/examples/tests/eeg_metrics_ci.py --board-id -1 --classifier 2 --metric 2 --model-file $GITHUB_WORKSPACE/src/ml/train/logreg_mindfulness.onnx + - name: Moving Average Classifier Python + run: sudo -H python3 $GITHUB_WORKSPACE/python_package/examples/tests/moving_average_classifier.py - name: EEG Metrics Cpp run: $GITHUB_WORKSPACE/cpp_package/examples/ml/build/eeg_metrics --board-id -1 env: diff --git a/.github/workflows/run_windows.yml b/.github/workflows/run_windows.yml index 344e510cc..8ec679a66 100644 --- a/.github/workflows/run_windows.yml +++ b/.github/workflows/run_windows.yml @@ -237,6 +237,7 @@ jobs: shell: cmd - name: PSD Welch Nyquist Overlap Python Test run: python %GITHUB_WORKSPACE%\python_package\examples\tests\psd_welch_nyquist_overlap.py + shell: cmd - name: Wavelet Buffer Size Python Test run: python %GITHUB_WORKSPACE%\python_package\examples\tests\wavelet_buffer_size.py shell: cmd @@ -280,6 +281,9 @@ jobs: - name: EEG Metrics CI Python Test run: python %GITHUB_WORKSPACE%\python_package\examples\tests\eeg_metrics_ci.py --board-id -1 --metric 2 --classifier 2 --model-file %GITHUB_WORKSPACE%\src\ml\train\logreg_mindfulness.onnx shell: cmd + - name: Moving Average Classifier Python Test + run: python %GITHUB_WORKSPACE%\python_package\examples\tests\moving_average_classifier.py + shell: cmd - name: EEG Metrics CI Cpp Test run: .\cpp_package\examples\ml\build\Release\eeg_metrics_ci.exe --board-id -1 --metric 2 --classifier 2 --model-file %GITHUB_WORKSPACE%\src\ml\train\logreg_mindfulness.onnx shell: cmd diff --git a/csharp_package/brainflow/brainflow/ml_module_library.cs b/csharp_package/brainflow/brainflow/ml_module_library.cs index cb6de268c..a0542397b 100644 --- a/csharp_package/brainflow/brainflow/ml_module_library.cs +++ b/csharp_package/brainflow/brainflow/ml_module_library.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; diff --git a/matlab_package/brainflow/BrainFlowClassifiers.m b/matlab_package/brainflow/BrainFlowClassifiers.m index ee4028d91..b70e4202a 100644 --- a/matlab_package/brainflow/BrainFlowClassifiers.m +++ b/matlab_package/brainflow/BrainFlowClassifiers.m @@ -5,4 +5,4 @@ DYN_LIB_CLASSIFIER(1) ONNX_CLASSIFIER(2) end -end \ No newline at end of file +end diff --git a/python_package/brainflow/ml_model.py b/python_package/brainflow/ml_model.py index fe9480e33..0119f3de3 100644 --- a/python_package/brainflow/ml_model.py +++ b/python_package/brainflow/ml_model.py @@ -38,7 +38,7 @@ class BrainFlowModelParams(object): :type classifier: int :param file: file to load model :type file: str - :param other_info: additional information + :param other_info: additional information or configuration (e.g. moving average smoothing via `{"window_len": 5}` or `moving_average=5`). Note: moving average options in other_info are handled uniformly across all classifiers. :type other_info: str :param output_name: output node name :type output_name: str diff --git a/python_package/examples/tests/moving_average_classifier.py b/python_package/examples/tests/moving_average_classifier.py new file mode 100644 index 000000000..1f6c35138 --- /dev/null +++ b/python_package/examples/tests/moving_average_classifier.py @@ -0,0 +1,302 @@ +import os +import sys +import numpy as np + +# add python_package to sys.path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))) + +from brainflow.ml_model import MLModel, BrainFlowMetrics, BrainFlowClassifiers, BrainFlowModelParams + +V1 = np.array([0.1, 0.2, 0.3, 0.2, 0.2], dtype=np.float64) +V2 = np.array([0.5, 0.1, 0.1, 0.1, 0.2], dtype=np.float64) + + +def test_baseline_raw_predictions(): + params = BrainFlowModelParams( + BrainFlowMetrics.MINDFULNESS.value, + BrainFlowClassifiers.DEFAULT_CLASSIFIER.value + ) + model = MLModel(params) + model.prepare() + raw_1 = model.predict(V1)[0] + raw_2 = model.predict(V2)[0] + model.release() + + assert raw_1 != raw_2 + return raw_1, raw_2 + + +def test_json_explicit_window_len(): + raw_1, raw_2 = test_baseline_raw_predictions() + + params = BrainFlowModelParams( + BrainFlowMetrics.MINDFULNESS.value, + BrainFlowClassifiers.DEFAULT_CLASSIFIER.value + ) + params.other_info = '{"window_len": 3}' + model = MLModel(params) + model.prepare() + + # Step 1: In=v1 -> out1 = raw1 + out1 = model.predict(V1)[0] + assert np.isclose(out1, raw_1) + + # Step 2: In=v2 -> out2 = (raw1 + raw2) / 2 + out2 = model.predict(V2)[0] + assert np.isclose(out2, (raw_1 + raw_2) / 2.0) + + # Step 3: In=v2 -> out3 = (raw1 + 2 * raw2) / 3 + out3 = model.predict(V2)[0] + assert np.isclose(out3, (raw_1 + 2.0 * raw_2) / 3.0) + + # Step 4: In=v2 -> out4 = raw2 (oldest raw1 popped) + out4 = model.predict(V2)[0] + assert np.isclose(out4, raw_2) + + model.release() + + +def test_json_moving_average_with_window_len(): + raw_1, raw_2 = test_baseline_raw_predictions() + + params = BrainFlowModelParams( + BrainFlowMetrics.MINDFULNESS.value, + BrainFlowClassifiers.DEFAULT_CLASSIFIER.value + ) + params.other_info = '{"moving_average": true, "window_len": 2}' + model = MLModel(params) + model.prepare() + + out1 = model.predict(V1)[0] + assert np.isclose(out1, raw_1) + + out2 = model.predict(V2)[0] + assert np.isclose(out2, (raw_1 + raw_2) / 2.0) + + model.release() + + +def test_json_moving_average_integer(): + raw_1, raw_2 = test_baseline_raw_predictions() + + params = BrainFlowModelParams( + BrainFlowMetrics.MINDFULNESS.value, + BrainFlowClassifiers.DEFAULT_CLASSIFIER.value + ) + params.other_info = '{"moving_average": 3}' + model = MLModel(params) + model.prepare() + + out1 = model.predict(V1)[0] + assert np.isclose(out1, raw_1) + + out2 = model.predict(V2)[0] + assert np.isclose(out2, (raw_1 + raw_2) / 2.0) + + model.release() + + +def test_json_default_window(): + raw_1, raw_2 = test_baseline_raw_predictions() + + params = BrainFlowModelParams( + BrainFlowMetrics.MINDFULNESS.value, + BrainFlowClassifiers.DEFAULT_CLASSIFIER.value + ) + params.other_info = '{"moving_average": true}' + model = MLModel(params) + model.prepare() + + for _ in range(5): + model.predict(V1) + + out6 = model.predict(V2)[0] + assert np.isclose(out6, (4.0 * raw_1 + raw_2) / 5.0) + + model.release() + + +def test_json_explicitly_disabled(): + raw_1, raw_2 = test_baseline_raw_predictions() + + # Case 1: '{"moving_average": false}' + params = BrainFlowModelParams( + BrainFlowMetrics.MINDFULNESS.value, + BrainFlowClassifiers.DEFAULT_CLASSIFIER.value + ) + params.other_info = '{"moving_average": false}' + model = MLModel(params) + model.prepare() + model.predict(V1) + out2 = model.predict(V2)[0] + assert np.isclose(out2, raw_2) + model.release() + + # Case 2: '{"moving_average": false, "window_len": 3}' (explicit false takes precedence) + params.other_info = '{"moving_average": false, "window_len": 3}' + model = MLModel(params) + model.prepare() + model.predict(V1) + out2 = model.predict(V2)[0] + assert np.isclose(out2, raw_2) + model.release() + + +def test_json_window_size_one(): + raw_1, raw_2 = test_baseline_raw_predictions() + + # Case 1: '{"window_len": 1}' + params = BrainFlowModelParams( + BrainFlowMetrics.MINDFULNESS.value, + BrainFlowClassifiers.DEFAULT_CLASSIFIER.value + ) + params.other_info = '{"window_len": 1}' + model = MLModel(params) + model.prepare() + model.predict(V1) + out2 = model.predict(V2)[0] + assert np.isclose(out2, raw_2) + model.release() + + # Case 2: '{"moving_average": 1}' + params.other_info = '{"moving_average": 1}' + model = MLModel(params) + model.prepare() + model.predict(V1) + out2 = model.predict(V2)[0] + assert np.isclose(out2, raw_2) + model.release() + + +def test_unrelated_json_config_ignored(): + raw_1, raw_2 = test_baseline_raw_predictions() + + params = BrainFlowModelParams( + BrainFlowMetrics.MINDFULNESS.value, + BrainFlowClassifiers.DEFAULT_CLASSIFIER.value + ) + params.other_info = '{"unrelated_key": "some_value"}' + model = MLModel(params) + model.prepare() + model.predict(V1) + out2 = model.predict(V2)[0] + assert np.isclose(out2, raw_2) + model.release() + + +def test_non_json_string_ignored(): + raw_1, raw_2 = test_baseline_raw_predictions() + + for config in ["3", "channel_3", "moving_average=3"]: + params = BrainFlowModelParams( + BrainFlowMetrics.MINDFULNESS.value, + BrainFlowClassifiers.DEFAULT_CLASSIFIER.value + ) + params.other_info = config + model = MLModel(params) + model.prepare() + model.predict(V1) + out2 = model.predict(V2)[0] + assert np.isclose(out2, raw_2) + model.release() + + +def test_restfulness_moving_average(): + raw_1, raw_2 = test_baseline_raw_predictions() + + rf_params = BrainFlowModelParams( + BrainFlowMetrics.RESTFULNESS.value, + BrainFlowClassifiers.DEFAULT_CLASSIFIER.value + ) + rf_params.other_info = '{"moving_average": true, "window_len": 2}' + rf_model = MLModel(rf_params) + rf_model.prepare() + + raw_rf_1 = 1.0 - raw_1 + raw_rf_2 = 1.0 - raw_2 + + rf_out1 = rf_model.predict(V1)[0] + assert np.isclose(rf_out1, raw_rf_1) + + rf_out2 = rf_model.predict(V2)[0] + assert np.isclose(rf_out2, (raw_rf_1 + raw_rf_2) / 2.0) + + rf_model.release() + + +def test_vector_output_moving_average(): + import platform + import shutil + import subprocess + import tempfile + + cc = shutil.which('clang') or shutil.which('gcc') or shutil.which('cc') + if not cc: + print("C compiler not available, skipping DynLibClassifier vector test.") + return + + with tempfile.TemporaryDirectory() as tmpdir: + c_code = """ +#if defined(_WIN32) +#define EXPORT __declspec(dllexport) +#else +#define EXPORT __attribute__((visibility("default"))) +#endif + +EXPORT int prepare(void *cls, void *params) { return 0; } +EXPORT int predict(double *data, int data_len, double *output, int *output_len, void *params) { + output[0] = (data_len > 0) ? data[0] : 1.0; + output[1] = (data_len > 1) ? data[1] * 2.0 : 2.0; + output[2] = (data_len > 2) ? data[2] * 3.0 : 3.0; + *output_len = 3; + return 0; +} +EXPORT int release(void *params) { return 0; } +""" + c_file = os.path.join(tmpdir, 'plugin.c') + ext = '.dylib' if platform.system() == 'Darwin' else ('.dll' if platform.system() == 'Windows' else '.so') + so_file = os.path.join(tmpdir, 'libplugin' + ext) + with open(c_file, 'w') as f: + f.write(c_code) + cmd = [cc, '-shared', '-fPIC', c_file, '-o', so_file] + if subprocess.call(cmd) == 0: + vec_params = BrainFlowModelParams( + BrainFlowMetrics.USER_DEFINED.value, + BrainFlowClassifiers.DYN_LIB_CLASSIFIER.value + ) + vec_params.file = so_file + vec_params.other_info = '{"window_len": 2}' + vec_model = MLModel(vec_params) + vec_model.prepare() + + # Feed sample 1: [10, 10, 10] -> plugin returns [10, 20, 30] + vec_out1 = vec_model.predict(np.array([10.0, 10.0, 10.0], dtype=np.float64)) + assert len(vec_out1) == 3 + assert np.allclose(vec_out1, [10.0, 20.0, 30.0]) + + # Feed sample 2: [20, 20, 20] -> plugin returns [20, 40, 60] -> window avg = [15, 30, 45] + vec_out2 = vec_model.predict(np.array([20.0, 20.0, 20.0], dtype=np.float64)) + assert len(vec_out2) == 3 + assert np.allclose(vec_out2, [15.0, 30.0, 45.0]) + + # Feed sample 3: [20, 20, 20] -> window pops sample 1 -> avg = [20, 40, 60] + vec_out3 = vec_model.predict(np.array([20.0, 20.0, 20.0], dtype=np.float64)) + assert len(vec_out3) == 3 + assert np.allclose(vec_out3, [20.0, 40.0, 60.0]) + + vec_model.release() + + +if __name__ == '__main__': + test_baseline_raw_predictions() + test_json_explicit_window_len() + test_json_moving_average_with_window_len() + test_json_moving_average_integer() + test_json_default_window() + test_json_explicitly_disabled() + test_json_window_size_one() + test_unrelated_json_config_ignored() + test_non_json_string_ignored() + test_restfulness_moving_average() + test_vector_output_moving_average() + print("All moving average classifier tests passed successfully!") diff --git a/src/ml/base_classifier.cpp b/src/ml/base_classifier.cpp index 7d227ac6d..245867301 100644 --- a/src/ml/base_classifier.cpp +++ b/src/ml/base_classifier.cpp @@ -1,7 +1,12 @@ +#include +#include + #include "base_classifier.h" #include "brainflow_constants.h" +#include "json.hpp" #include "spdlog/sinks/null_sink.h" +using json = nlohmann::json; #define LOGGER_NAME "ml_logger" @@ -60,3 +65,132 @@ int BaseClassifier::set_log_file (const char *log_file) return (int)BrainFlowExitCodes::STATUS_OK; #endif } + +void BaseClassifier::parse_moving_average_params () +{ + use_moving_average = false; + moving_average_window = 0; + + if (params.other_info.empty ()) + { + return; + } + + try + { + json j = json::parse (params.other_info); + if (!j.is_object ()) + { + return; + } + + if (j.contains ("moving_average")) + { + if (j["moving_average"].is_boolean ()) + { + if (!j["moving_average"].get ()) + { + return; + } + use_moving_average = true; + moving_average_window = DEFAULT_MOVING_AVERAGE_WINDOW; + } + else if (j["moving_average"].is_number_integer ()) + { + int val = j["moving_average"].get (); + if (val <= 0) + { + return; + } + use_moving_average = true; + moving_average_window = val; + } + } + + if (j.contains ("window_len") && j["window_len"].is_number_integer ()) + { + int val = j["window_len"].get (); + if (val > 0) + { + moving_average_window = val; + use_moving_average = true; + } + else + { + use_moving_average = false; + moving_average_window = 0; + } + } + } + catch (const std::exception &) + { + use_moving_average = false; + moving_average_window = 0; + } +} + +void BaseClassifier::reset_moving_average () +{ + window_data.clear (); +} + +void BaseClassifier::apply_moving_average (double *output, int *output_len) +{ + if ((output == NULL) || (output_len == NULL) || (*output_len <= 0) || (moving_average_window <= 0)) + { + return; + } + + if (window_data.empty () || ((int)window_data.front ().size () != *output_len)) + { + window_data.clear (); + } + + window_data.push_back (std::vector (output, output + *output_len)); + while ((int)window_data.size () > moving_average_window) + { + window_data.pop_front (); + } + + if (window_data.empty ()) + { + return; + } + + for (int i = 0; i < *output_len; i++) + { + double sum = 0.0; + for (size_t w = 0; w < window_data.size (); w++) + { + sum += window_data[w][i]; + } + output[i] = sum / window_data.size (); + } +} + +int BaseClassifier::prepare () +{ + parse_moving_average_params (); + reset_moving_average (); + return prepare_classifier (); +} + +int BaseClassifier::predict (double *data, int data_len, double *output, int *output_len) +{ + int res = calculate (data, data_len, output, output_len); + if (res != (int)BrainFlowExitCodes::STATUS_OK) + { + return res; + } + if (use_moving_average) + { + apply_moving_average (output, output_len); + } + return (int)BrainFlowExitCodes::STATUS_OK; +} + +int BaseClassifier::release () +{ + reset_moving_average (); + return release_classifier (); +} diff --git a/src/ml/dyn_lib_classifier.cpp b/src/ml/dyn_lib_classifier.cpp index 269afbd0b..5578efd12 100644 --- a/src/ml/dyn_lib_classifier.cpp +++ b/src/ml/dyn_lib_classifier.cpp @@ -2,7 +2,7 @@ #include "brainflow_constants.h" -int DynLibClassifier::prepare () +int DynLibClassifier::prepare_classifier () { if (dll_loader != NULL) { @@ -33,7 +33,7 @@ int DynLibClassifier::prepare () return func ((void *)this, ¶ms); } -int DynLibClassifier::predict (double *data, int data_len, double *output, int *output_len) +int DynLibClassifier::calculate (double *data, int data_len, double *output, int *output_len) { if (dll_loader == NULL) { @@ -50,7 +50,7 @@ int DynLibClassifier::predict (double *data, int data_len, double *output, int * return func (data, data_len, output, output_len, ¶ms); } -int DynLibClassifier::release () +int DynLibClassifier::release_classifier () { if (dll_loader == NULL) { diff --git a/src/ml/inc/base_classifier.h b/src/ml/inc/base_classifier.h index ccd7abd33..b287481ae 100644 --- a/src/ml/inc/base_classifier.h +++ b/src/ml/inc/base_classifier.h @@ -1,8 +1,15 @@ #pragma once +#include +#include +#include + +#include "brainflow_constants.h" #include "brainflow_model_params.h" #include "spdlog/spdlog.h" +#define DEFAULT_MOVING_AVERAGE_WINDOW 5 + class BaseClassifier { public: @@ -16,12 +23,15 @@ class BaseClassifier BaseClassifier (struct BrainFlowModelParams model_params) : params (model_params) { skip_logs = false; + use_moving_average = false; + moving_average_window = 0; } virtual ~BaseClassifier () { skip_logs = true; } + // Classifier ml_logger should not be called from destructors, to ensure that there are safe log // methods Classifierml_logger still available but should be used only outside destructors template @@ -45,7 +55,26 @@ class BaseClassifier } } - virtual int prepare () = 0; - virtual int predict (double *data, int data_len, double *output, int *output_len) = 0; - virtual int release () = 0; + int prepare (); + int predict (double *data, int data_len, double *output, int *output_len); + int release (); + +protected: + bool use_moving_average; + int moving_average_window; + std::deque> window_data; + + void parse_moving_average_params (); + void reset_moving_average (); + void apply_moving_average (double *output, int *output_len); + + virtual int prepare_classifier () + { + return (int)BrainFlowExitCodes::STATUS_OK; + } + virtual int calculate (double *data, int data_len, double *output, int *output_len) = 0; + virtual int release_classifier () + { + return (int)BrainFlowExitCodes::STATUS_OK; + } }; diff --git a/src/ml/inc/dyn_lib_classifier.h b/src/ml/inc/dyn_lib_classifier.h index 28a075eff..c2b4e8c14 100644 --- a/src/ml/inc/dyn_lib_classifier.h +++ b/src/ml/inc/dyn_lib_classifier.h @@ -6,6 +6,11 @@ #include "runtime_dll_loader.h" +// DynLibClassifier loads user-provided shared libraries (.so / .dll / .dylib) exporting +// "prepare", "predict", and "release" C functions. +// Note for plugin authors: BaseClassifier inspects params.other_info for moving average +// configuration via JSON. Recognized keys: "moving_average" (bool or int) and "window_len" (int). +// Non-JSON strings are safely ignored and will not activate smoothing. class DynLibClassifier : public BaseClassifier { public: @@ -20,11 +25,11 @@ class DynLibClassifier : public BaseClassifier release (); } - int prepare () override; - int predict (double *data, int data_len, double *output, int *output_len) override; - int release () override; - protected: + int prepare_classifier () override; + int calculate (double *data, int data_len, double *output, int *output_len) override; + int release_classifier () override; + virtual std::string get_dyn_lib_path () { return params.file; diff --git a/src/ml/inc/mindfulness_classifier.h b/src/ml/inc/mindfulness_classifier.h index 0f6007be5..4958d09c8 100644 --- a/src/ml/inc/mindfulness_classifier.h +++ b/src/ml/inc/mindfulness_classifier.h @@ -16,7 +16,6 @@ class MindfulnessClassifier : public BaseClassifier release (); } - int prepare () override; - int predict (double *data, int data_len, double *output, int *output_len) override; - int release () override; +protected: + int calculate (double *data, int data_len, double *output, int *output_len) override; }; diff --git a/src/ml/inc/restfulness_classifier.h b/src/ml/inc/restfulness_classifier.h index dcfa9252f..6c97ce3b2 100644 --- a/src/ml/inc/restfulness_classifier.h +++ b/src/ml/inc/restfulness_classifier.h @@ -11,9 +11,10 @@ class RestfulnessClassifier : public MindfulnessClassifier { } - int predict (double *data, int data_len, double *output, int *output_len) override +protected: + int calculate (double *data, int data_len, double *output, int *output_len) override { - int res = MindfulnessClassifier::predict (data, data_len, output, output_len); + int res = MindfulnessClassifier::calculate (data, data_len, output, output_len); if (res != (int)BrainFlowExitCodes::STATUS_OK) { return res; diff --git a/src/ml/mindfulness_classifier.cpp b/src/ml/mindfulness_classifier.cpp index 62c52c611..6f1ea55ac 100644 --- a/src/ml/mindfulness_classifier.cpp +++ b/src/ml/mindfulness_classifier.cpp @@ -7,12 +7,8 @@ #include "mindfulness_model.h" -int MindfulnessClassifier::prepare () -{ - return (int)BrainFlowExitCodes::STATUS_OK; -} - -int MindfulnessClassifier::predict (double *data, int data_len, double *output, int *output_len) +int MindfulnessClassifier::calculate ( + double *data, int data_len, double *output, int *output_len) { if ((data_len < 5) || (data == NULL) || (output == NULL)) { @@ -30,8 +26,3 @@ int MindfulnessClassifier::predict (double *data, int data_len, double *output, *output_len = 1; return (int)BrainFlowExitCodes::STATUS_OK; } - -int MindfulnessClassifier::release () -{ - return (int)BrainFlowExitCodes::STATUS_OK; -} \ No newline at end of file diff --git a/src/ml/onnx/inc/onnx_classifier.h b/src/ml/onnx/inc/onnx_classifier.h index 18b8cc216..525769220 100644 --- a/src/ml/onnx/inc/onnx_classifier.h +++ b/src/ml/onnx/inc/onnx_classifier.h @@ -51,7 +51,8 @@ class OnnxClassifier : public BaseClassifier release (); } - int prepare () override; - int predict (double *data, int data_len, double *output, int *output_len) override; - int release () override; +protected: + int prepare_classifier () override; + int calculate (double *data, int data_len, double *output, int *output_len) override; + int release_classifier () override; }; diff --git a/src/ml/onnx/onnx_classifier.cpp b/src/ml/onnx/onnx_classifier.cpp index 02d99724b..eb2678eb5 100644 --- a/src/ml/onnx/onnx_classifier.cpp +++ b/src/ml/onnx/onnx_classifier.cpp @@ -14,7 +14,7 @@ void log_onnx_msg (void *param, OrtLoggingLevel severity, const char *category, } } -int OnnxClassifier::prepare () +int OnnxClassifier::prepare_classifier () { if (dll_loader != NULL) { @@ -62,7 +62,7 @@ int OnnxClassifier::prepare () } -int OnnxClassifier::predict (double *data, int data_len, double *output, int *output_len) +int OnnxClassifier::calculate (double *data, int data_len, double *output, int *output_len) { int res = (int)BrainFlowExitCodes::STATUS_OK; if (ort == NULL) @@ -390,7 +390,7 @@ int OnnxClassifier::predict (double *data, int data_len, double *output, int *ou return res; } -int OnnxClassifier::release () +int OnnxClassifier::release_classifier () { if ((allocator != NULL) && (ort != NULL)) {