Skip to content

Commit 709f270

Browse files
committed
Number of TF patches, image classification without external buffers, remove uTensor
1 parent 67f085e commit 709f270

File tree

289 files changed

+576
-9209
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

289 files changed

+576
-9209
lines changed

.mbedignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ tensorflow/lite/micro/mbed/
33
porting/arduino/
44
porting/ecm3532/
55
porting/posix/
6+
porting/silabs/
67
porting/stm32-cubeai/
78
classifier/ei_run_classifier_c*

CMSIS/NN/Source/NNSupportFunctions/arm_nn_mat_mult_nt_t_s8.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
__STATIC_FORCEINLINE uint32_t __patched_SXTB16_RORn(uint32_t op1, uint32_t rotate) {
3838
uint32_t result;
3939
if (__builtin_constant_p (rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) {
40-
asm volatile ("sxtb16 %0, %1, ROR %2" : "=r" (result) : "r" (op1), "i" (rotate) );
40+
__ASM volatile ("sxtb16 %0, %1, ROR %2" : "=r" (result) : "r" (op1), "i" (rotate) );
4141
} else {
4242
result = __SXTB16(__ROR(op1, rotate)) ;
4343
}

classifier/ei_run_classifier.h

+304-167
Large diffs are not rendered by default.

classifier/ei_run_classifier_image.h

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef _EDGE_IMPULSE_RUN_CLASSIFIER_IMAGE_H_
2+
#define _EDGE_IMPULSE_RUN_CLASSIFIER_IMAGE_H_
3+
4+
#include "ei_run_classifier.h"
5+
6+
7+
8+
#endif // _EDGE_IMPULSE_RUN_CLASSIFIER_IMAGE_H_

classifier/ei_run_dsp.h

+72-17
Original file line numberDiff line numberDiff line change
@@ -477,49 +477,104 @@ __attribute__((unused)) int extract_image_features(signal_t *signal, matrix_t *o
477477

478478
int16_t channel_count = strcmp(config.channels, "Grayscale") == 0 ? 1 : 3;
479479

480-
if (output_matrix->rows * output_matrix->cols != EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT * channel_count) {
480+
if (output_matrix->rows * output_matrix->cols != static_cast<uint32_t>(EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT * channel_count)) {
481481
ei_printf("out_matrix = %hu items\n", output_matrix->rows, output_matrix->cols);
482-
ei_printf("calculated size = %hu items\n", EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT * channel_count);
482+
ei_printf("calculated size = %hu items\n", static_cast<uint32_t>(EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT * channel_count));
483483
EIDSP_ERR(EIDSP_MATRIX_SIZE_MISMATCH);
484484
}
485485

486486
size_t output_ix = 0;
487487

488488
// buffered read from the signal
489489
size_t bytes_left = signal->total_length;
490-
for (size_t ix = 0; ix < signal->total_length; ix += 4096) {
491-
size_t bytes_to_read = bytes_left > 4096 ? 4096 : bytes_left;
490+
for (size_t ix = 0; ix < signal->total_length; ix += 1024) {
491+
size_t elements_to_read = bytes_left > 1024 ? 1024 : bytes_left;
492492

493-
matrix_t input_matrix(bytes_to_read, config.axes);
493+
matrix_t input_matrix(elements_to_read, config.axes);
494494
if (!input_matrix.buffer) {
495495
EIDSP_ERR(EIDSP_OUT_OF_MEM);
496496
}
497-
signal->get_data(ix, bytes_to_read, input_matrix.buffer);
497+
signal->get_data(ix, elements_to_read, input_matrix.buffer);
498498

499-
for (size_t jx = 0; jx < bytes_to_read; jx++) {
499+
for (size_t jx = 0; jx < elements_to_read; jx++) {
500500
uint32_t pixel = static_cast<uint32_t>(input_matrix.buffer[jx]);
501+
502+
// rgb to 0..1
503+
float r = static_cast<float>(pixel >> 16 & 0xff) / 255.0f;
504+
float g = static_cast<float>(pixel >> 8 & 0xff) / 255.0f;
505+
float b = static_cast<float>(pixel & 0xff) / 255.0f;
506+
507+
if (channel_count == 3) {
508+
output_matrix->buffer[output_ix++] = r;
509+
output_matrix->buffer[output_ix++] = g;
510+
output_matrix->buffer[output_ix++] = b;
511+
}
512+
else {
513+
// ITU-R 601-2 luma transform
514+
// see: https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.convert
515+
float v = (0.299f * r) + (0.587f * g) + (0.114f * b);
516+
output_matrix->buffer[output_ix++] = v;
517+
}
518+
}
519+
520+
bytes_left -= elements_to_read;
521+
}
522+
523+
return EIDSP_OK;
524+
}
525+
526+
#if EI_CLASSIFIER_TFLITE_INPUT_QUANTIZED == 1
527+
__attribute__((unused)) int extract_image_features_quantized(signal_t *signal, matrix_i8_t *output_matrix, void *config_ptr) {
528+
ei_dsp_config_image_t config = *((ei_dsp_config_image_t*)config_ptr);
529+
530+
int16_t channel_count = strcmp(config.channels, "Grayscale") == 0 ? 1 : 3;
531+
532+
if (output_matrix->rows * output_matrix->cols != static_cast<uint32_t>(EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT * channel_count)) {
533+
ei_printf("out_matrix = %hu items\n", output_matrix->rows, output_matrix->cols);
534+
ei_printf("calculated size = %hu items\n", static_cast<uint32_t>(EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT * channel_count));
535+
EIDSP_ERR(EIDSP_MATRIX_SIZE_MISMATCH);
536+
}
537+
538+
size_t output_ix = 0;
539+
540+
// buffered read from the signal
541+
size_t bytes_left = signal->total_length;
542+
for (size_t ix = 0; ix < signal->total_length; ix += 1024) {
543+
size_t elements_to_read = bytes_left > 1024 ? 1024 : bytes_left;
544+
545+
matrix_t input_matrix(elements_to_read, config.axes);
546+
if (!input_matrix.buffer) {
547+
EIDSP_ERR(EIDSP_OUT_OF_MEM);
548+
}
549+
signal->get_data(ix, elements_to_read, input_matrix.buffer);
550+
551+
for (size_t jx = 0; jx < elements_to_read; jx++) {
552+
uint32_t pixel = static_cast<uint32_t>(input_matrix.buffer[jx]);
553+
554+
// rgb to 0..1
555+
float r = static_cast<float>(pixel >> 16 & 0xff) / 255.0f;
556+
float g = static_cast<float>(pixel >> 8 & 0xff) / 255.0f;
557+
float b = static_cast<float>(pixel & 0xff) / 255.0f;
558+
501559
if (channel_count == 3) {
502-
// rgb to 0..1
503-
output_matrix->buffer[output_ix++] = static_cast<float>(pixel >> 16 & 0xff) / 255.0f;
504-
output_matrix->buffer[output_ix++] = static_cast<float>(pixel >> 8 & 0xff) / 255.0f;
505-
output_matrix->buffer[output_ix++] = static_cast<float>(pixel & 0xff) / 255.0f;
560+
output_matrix->buffer[output_ix++] = static_cast<int8_t>(round(r / EI_CLASSIFIER_TFLITE_INPUT_SCALE) + EI_CLASSIFIER_TFLITE_INPUT_ZEROPOINT);
561+
output_matrix->buffer[output_ix++] = static_cast<int8_t>(round(g / EI_CLASSIFIER_TFLITE_INPUT_SCALE) + EI_CLASSIFIER_TFLITE_INPUT_ZEROPOINT);
562+
output_matrix->buffer[output_ix++] = static_cast<int8_t>(round(b / EI_CLASSIFIER_TFLITE_INPUT_SCALE) + EI_CLASSIFIER_TFLITE_INPUT_ZEROPOINT);
506563
}
507564
else {
508-
// grayscale conversion (also to 0..1)
509-
float r = static_cast<float>(pixel >> 16 & 0xff) / 255.0f;
510-
float g = static_cast<float>(pixel >> 8 & 0xff) / 255.0f;
511-
float b = static_cast<float>(pixel & 0xff) / 255.0f;
512565
// ITU-R 601-2 luma transform
513566
// see: https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.convert
514-
output_matrix->buffer[output_ix++] = (0.299f * r) + (0.587f * g) + (0.114f * b);
567+
float v = (0.299f * r) + (0.587f * g) + (0.114f * b);
568+
output_matrix->buffer[output_ix++] = static_cast<int8_t>(round(v / EI_CLASSIFIER_TFLITE_INPUT_SCALE) + EI_CLASSIFIER_TFLITE_INPUT_ZEROPOINT);
515569
}
516570
}
517571

518-
bytes_left -= bytes_to_read;
572+
bytes_left -= elements_to_read;
519573
}
520574

521575
return EIDSP_OK;
522576
}
577+
#endif // EI_CLASSIFIER_TFLITE_INPUT_QUANTIZED == 1
523578

524579
#ifdef __cplusplus
525580
}

create-arduino-library.sh

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ rm -rf $SCRIPTPATH/tensorflow/lite/micro/mbed/
2424
rm -rf $SCRIPTPATH/porting/ecm3532/
2525
rm -rf $SCRIPTPATH/porting/mbed/
2626
rm -rf $SCRIPTPATH/porting/posix/
27+
rm -rf $SCRIPTPATH/porting/silabs/
2728
rm -rf $SCRIPTPATH/porting/stm32-cubeai/
2829
rm -rf $SCRIPTPATH/classifier/ei_run_classifier_c*
2930
rm -rf $SCRIPTPATH/CMSIS/DSP/Source/TransformFunctions/arm_bitreversal2.S

dsp/numpy_types.h

+78
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,84 @@ typedef struct ei_matrix {
126126
#endif // #ifdef __cplusplus
127127
} matrix_t;
128128

129+
/**
130+
* A matrix structure that allocates a matrix on the **heap**.
131+
* Freeing happens by calling `delete` on the object or letting the object go out of scope.
132+
*/
133+
typedef struct ei_matrix_i8 {
134+
int8_t *buffer;
135+
uint32_t rows;
136+
uint32_t cols;
137+
bool buffer_managed_by_me;
138+
139+
#if EIDSP_TRACK_ALLOCATIONS
140+
const char *_fn;
141+
const char *_file;
142+
int _line;
143+
#endif
144+
145+
#ifdef __cplusplus
146+
/**
147+
* Create a new matrix
148+
* @param n_rows Number of rows
149+
* @param n_cols Number of columns
150+
* @param a_buffer Buffer, if not provided we'll alloc on the heap
151+
*/
152+
ei_matrix_i8(
153+
uint32_t n_rows,
154+
uint32_t n_cols,
155+
int8_t *a_buffer = NULL
156+
#if EIDSP_TRACK_ALLOCATIONS
157+
,
158+
const char *fn = NULL,
159+
const char *file = NULL,
160+
int line = 0
161+
#endif
162+
)
163+
{
164+
if (a_buffer) {
165+
buffer = a_buffer;
166+
buffer_managed_by_me = false;
167+
}
168+
else {
169+
buffer = (int8_t*)calloc(n_rows * n_cols * sizeof(int8_t), 1);
170+
buffer_managed_by_me = true;
171+
}
172+
rows = n_rows;
173+
cols = n_cols;
174+
175+
if (!a_buffer) {
176+
#if EIDSP_TRACK_ALLOCATIONS
177+
_fn = fn;
178+
_file = file;
179+
_line = line;
180+
if (_fn) {
181+
ei_dsp_register_matrix_alloc_internal(fn, file, line, rows, cols, sizeof(int8_t));
182+
}
183+
else {
184+
ei_dsp_register_matrix_alloc(rows, cols, sizeof(int8_t));
185+
}
186+
#endif
187+
}
188+
}
189+
190+
~ei_matrix_i8() {
191+
if (buffer && buffer_managed_by_me) {
192+
free(buffer);
193+
194+
#if EIDSP_TRACK_ALLOCATIONS
195+
if (_fn) {
196+
ei_dsp_register_matrix_free_internal(_fn, _file, _line, rows, cols, sizeof(int8_t));
197+
}
198+
else {
199+
ei_dsp_register_matrix_free(rows, cols, sizeof(int8_t));
200+
}
201+
#endif
202+
}
203+
}
204+
#endif // #ifdef __cplusplus
205+
} matrix_i8_t;
206+
129207
/**
130208
* Another matrix structure that allocates a matrix on the **heap**.
131209
* Freeing happens by calling `delete` on the object or letting the object go out of scope.

porting/ei_classifier_porting.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ typedef enum {
3838
EI_IMPULSE_DSP_ERROR = -5,
3939
EI_IMPULSE_TFLITE_ARENA_ALLOC_FAILED = -6,
4040
EI_IMPULSE_CUBEAI_ERROR = -7,
41-
EI_IMPULSE_ALLOC_FAILED = -8
41+
EI_IMPULSE_ALLOC_FAILED = -8,
42+
EI_IMPULSE_ONLY_SUPPORTED_FOR_IMAGES = -9,
43+
EI_IMPULSE_UNSUPPORTED_INFERENCING_ENGINE = -10
4244
} EI_IMPULSE_ERROR;
4345

4446
/**

porting/silabs/debug_log.cpp

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/* Edge Impulse inferencing library
2+
* Copyright (c) 2020 EdgeImpulse Inc.
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
#include "tensorflow/lite/micro/debug_log.h"
24+
#include "../ei_classifier_porting.h"
25+
#include <stdio.h>
26+
#include <stdarg.h>
27+
28+
// Redirect TFLite DebugLog to ei_printf
29+
#if defined(__cplusplus) && EI_C_LINKAGE == 1
30+
extern "C"
31+
#endif // defined(__cplusplus) && EI_C_LINKAGE == 1
32+
void DebugLog(const char* s) {
33+
ei_printf("%s", s);
34+
}
+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/* Edge Impulse inferencing library
2+
* Copyright (c) 2020 EdgeImpulse Inc.
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
#include <stdarg.h>
24+
#include "../ei_classifier_porting.h"
25+
#include "ingestion-sdk-platform/SiliconLabs/ei_device_silabs_efm32mg.h"
26+
27+
28+
__attribute__((weak)) EI_IMPULSE_ERROR ei_run_impulse_check_canceled() {
29+
return EI_IMPULSE_OK;
30+
}
31+
32+
/**
33+
* Cancelable sleep, can be triggered with signal from other thread
34+
*/
35+
__attribute__((weak)) EI_IMPULSE_ERROR ei_sleep(int32_t time_ms) {
36+
EiDevice.delay_ms(time_ms);
37+
return EI_IMPULSE_OK;
38+
}
39+
40+
uint64_t ei_read_timer_ms() {
41+
return EiDevice.get_ms();
42+
}
43+
44+
uint64_t ei_read_timer_us() {
45+
46+
return 0;//EiDevice.get_ms() * 1000;
47+
}
48+
49+
__attribute__((weak)) void ei_printf(const char *format, ...) {
50+
va_list myargs;
51+
va_start(myargs, format);
52+
vprintf(format, myargs);
53+
va_end(myargs);
54+
}
55+
56+
__attribute__((weak)) void ei_printf_float(float f) {
57+
ei_printf("%f", f);
58+
}
59+
60+
#if defined(__cplusplus) && EI_C_LINKAGE == 1
61+
extern "C"
62+
#endif
63+
__attribute__((weak)) void DebugLog(const char* s) {
64+
ei_printf("%s", s);
65+
}

sources.txt

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@ The folders were taken from `tensorflow/lite/micro/tools/make/gen/osx_x86_64/prj
44
These files and directories were then deleted:
55
- `tensorflow/lite/micro/debug_log.cc`
66
- `tensorflow/lite/micro/examples/hello_world`
7-
utensor based on: https://github.com/janjongboom/utensor/#3a76b5870aee0a3d4ab3f17a9ed434fcca897520
8-
CMSIS-DSP based on: https://github.com/ARM-software/CMSIS_5/tree/4d378e81968c6bec5441a42885b24db7cf189bca
7+
CMSIS-DSP based on: https://github.com/ARM-software/CMSIS_5/tree/4d378e81968c6bec5441a42885b24db7cf189bca

tensorflow/lite/kernels/internal/max.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ limitations under the License.
1919

2020
namespace tflite {
2121

22-
#if defined(TF_LITE_USE_GLOBAL_MAX) || defined(__ZEPHYR__)
22+
// Patched by Edge Impulse, Arduino ESP32 targets have no std::fmax (at least on some targets)
23+
// see https://forum.edgeimpulse.com/t/esp32-cam-support/797/21
24+
#if defined(TF_LITE_USE_GLOBAL_MAX) || defined(__ZEPHYR__) || defined(ARDUINO_ARCH_ESP32)
2325
inline float TfLiteMax(const float& x, const float& y) {
2426
return std::max(x, y);
2527
}

tensorflow/lite/micro/micro_allocator.cc

+5-4
Original file line numberDiff line numberDiff line change
@@ -401,12 +401,13 @@ TfLiteStatus FlatBufferVectorToTfLiteTypeArray(
401401
kTfLiteArrayType** result) {
402402
TFLITE_DCHECK(error_reporter != nullptr);
403403
TFLITE_DCHECK(flatbuffer_array != nullptr);
404+
// Patched out by Edge Impulse, does not compile on ESP32 targets
404405
// Only two conversions are supported - float and int32 - ensure that these
405406
// match at compile time instead of duplicating functions here:
406-
static_assert((std::is_same<kFlatBufferVectorType, int32_t>() &&
407-
std::is_same<kTfLiteArrayType, TfLiteIntArray>()) ||
408-
(std::is_same<kFlatBufferVectorType, float>() &&
409-
std::is_same<kTfLiteArrayType, TfLiteFloatArray>()));
407+
// static_assert((std::is_same<kFlatBufferVectorType, int32_t>() &&
408+
// std::is_same<kTfLiteArrayType, TfLiteIntArray>()) ||
409+
// (std::is_same<kFlatBufferVectorType, float>() &&
410+
// std::is_same<kTfLiteArrayType, TfLiteFloatArray>()));
410411
if (FLATBUFFERS_LITTLEENDIAN) {
411412
// On little-endian machines, TfLite*Array happens to have the same memory
412413
// layout as flatbuffers:Vector<kFlatBufferVectorType>, so we can

0 commit comments

Comments
 (0)