1+ /* The Clear BSD License
2+ *
3+ * Copyright (c) 2025 EdgeImpulse Inc.
4+ * All rights reserved.
5+ *
6+ * Redistribution and use in source and binary forms, with or without
7+ * modification, are permitted (subject to the limitations in the disclaimer
8+ * below) provided that the following conditions are met:
9+ *
10+ * * Redistributions of source code must retain the above copyright notice,
11+ * this list of conditions and the following disclaimer.
12+ *
13+ * * Redistributions in binary form must reproduce the above copyright
14+ * notice, this list of conditions and the following disclaimer in the
15+ * documentation and/or other materials provided with the distribution.
16+ *
17+ * * Neither the name of the copyright holder nor the names of its
18+ * contributors may be used to endorse or promote products derived from this
19+ * software without specific prior written permission.
20+ *
21+ * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
22+ * THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
23+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
26+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
30+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32+ * POSSIBILITY OF SUCH DAMAGE.
33+ */
34+
35+ /* Includes ---------------------------------------------------------------- */
136#include < stdio.h>
237#include < stdarg.h>
338#include < cstring>
@@ -52,6 +87,10 @@ typedef struct {
5287#define ALIGN (X ) __align(X)
5388#endif
5489
90+ #if EI_CLASSIFIER_FREEFORM_OUTPUT
91+ static std::vector<matrix_t > freeform_outputs;
92+ #endif
93+
5594static char rapidjson_buffer[10 * 1024 * 1024 ] ALIGN(8 );
5695rapidjson::MemoryPoolAllocator<> rapidjson_allocator (rapidjson_buffer, sizeof (rapidjson_buffer));
5796
@@ -181,6 +220,17 @@ void json_send_classification_response(int id,
181220 }
182221#endif // EI_CLASSIFIER_HAS_VISUAL_ANOMALY
183222
223+ #if EI_CLASSIFIER_FREEFORM_OUTPUT
224+ nlohmann::json freeform_res = nlohmann::json::array ();
225+ for (size_t ix = 0 ; ix < freeform_outputs.size (); ix++) {
226+ const matrix_t & freeform_output = freeform_outputs[ix];
227+ nlohmann::json freeform_entry (
228+ std::vector<float >(freeform_output.buffer , freeform_output.buffer + (freeform_output.rows * freeform_output.cols ))
229+ );
230+ freeform_res.push_back (freeform_entry);
231+ }
232+ #endif // EI_CLASSIFIER_FREEFORM_OUTPUT
233+
184234 uint64_t total_ms = ei_read_timer_ms () - json_message_handler_entry_ms;
185235
186236 nlohmann::json resp = {
@@ -205,6 +255,9 @@ void json_send_classification_response(int id,
205255#if EI_CLASSIFIER_HAS_ANOMALY > 0
206256 {" anomaly" , result.anomaly },
207257#endif // EI_CLASSIFIER_HAS_ANOMALY == 1
258+ #if EI_CLASSIFIER_FREEFORM_OUTPUT
259+ {" freeform" , freeform_res},
260+ #endif // #if EI_CLASSIFIER_FREEFORM_OUTPUT
208261 }},
209262 {" timing" , {
210263 {" dsp" , result.timing .dsp },
@@ -267,6 +320,26 @@ void json_message_handler(rapidjson::Document &msg, char *resp_buffer, size_t re
267320
268321 run_classifier_init ();
269322
323+ #if EI_CLASSIFIER_FREEFORM_OUTPUT
324+ freeform_outputs.reserve (ei_default_impulse.impulse ->freeform_outputs_size );
325+ for (size_t ix = 0 ; ix < ei_default_impulse.impulse ->freeform_outputs_size ; ++ix) {
326+ freeform_outputs.emplace_back (ei_default_impulse.impulse ->freeform_outputs [ix], 1 );
327+ }
328+
329+ EI_IMPULSE_ERROR set_freeform_res = ei_set_freeform_output (freeform_outputs.data (), freeform_outputs.size ());
330+ if (set_freeform_res != EI_IMPULSE_OK) {
331+ char err_msg[128 ];
332+ snprintf (err_msg, 128 , " ei_set_freeform_output() failed with code %d" , set_freeform_res);
333+ nlohmann::json err = {
334+ {" id" , id},
335+ {" success" , false },
336+ {" error" , err_msg},
337+ };
338+ snprintf (resp_buffer, resp_buffer_size, " %s\n " , err.dump ().c_str ());
339+ return ;
340+ }
341+ #endif
342+
270343 vector<std::string> engine_properties;
271344 vector<std::string> labels;
272345 for (size_t ix = 0 ; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
@@ -292,7 +365,11 @@ void json_message_handler(rapidjson::Document &msg, char *resp_buffer, size_t re
292365 const char *model_type = " object_detection" ;
293366 #endif // EI_CLASSIFIER_OBJECT_DETECTION_LAST_LAYER
294367#else
368+ #if EI_CLASSIFIER_FREEFORM_OUTPUT
369+ const char *model_type = " freeform" ;
370+ #else
295371 const char *model_type = " classification" ;
372+ #endif
296373#endif // EI_CLASSIFIER_OBJECT_DETECTION
297374
298375 // keep track of configurable thresholds
@@ -418,6 +495,11 @@ void json_message_handler(rapidjson::Document &msg, char *resp_buffer, size_t re
418495 {" model_type" , model_type},
419496 {" slice_size" , EI_CLASSIFIER_SLICE_SIZE},
420497 {" use_continuous_mode" , EI_CLASSIFIER_SENSOR == EI_CLASSIFIER_SENSOR_MICROPHONE},
498+ #if EI_CLASSIFIER_CALIBRATION_ENABLED
499+ {" has_performance_calibration" , true },
500+ #else
501+ {" has_performance_calibration" , false },
502+ #endif // EI_CLASSIFIER_CALIBRATION_ENABLED
421503 {" inferencing_engine" , EI_CLASSIFIER_INFERENCING_ENGINE},
422504 {" thresholds" , thresholds},
423505 }},
0 commit comments