Hi I have completed a project on Edge Impulse for classifying Acute Lymphoblastic Leukaemia, and now converting the Arduino BLE 33 Sense camera example to run from an SD card. I can see in the Edge Impulse SDK that TfLiteTensor** input exists in tflite_micro.h and I need to access this via the main Arduino script, however I cannot seem to access this in code. Could you shed some light as getting a little lost in the sdk source. I also need to access interpreter TIA
I went a different way with this, but having issues. My model achieves 90% on the test data in Studio, I ad to reduce the inputs to 64 x64 due to ram:
The test data stored on the SD card is already resized and greyscaled, I read in the bytes from each image and store them, and the callback function raw_feature_get_data is passed to the signal struct. I am receiving always benign classification and always at 0.99609 confidence. It has been a sleepless night
Here is the code:
#include "Arduino.h"
#include <SPI.h>
#include <SD.h>
#include <Acute_Lymphoblastic_Leukaemia_Classifier_inferencing.h>
String images[]={
"1_B.JPG",
"2_B.JPG",
"3_B.JPG",
"4_B.JPG",
"5_B.JPG",
"1_PRE.JPG",
"2_PRE.JPG",
"3_PRE.JPG",
"4_PRE.JPG",
"5_PRE.JPG",
"1_PRO.JPG",
"2_PRO.JPG",
"3_PRO.JPG",
"4_PRO.JPG",
"5_PRO.JPG",
};
static bool debug_nn = false;
int8_t *image_data = NULL;
void setup()
{
Serial.begin(9600);
while (!Serial) {
;
}
Serial.println(F("Initialising SD card..."));
if (!SD.begin(10)) {
Serial.println(F("Initialisation failed!"));
return;
}
Serial.println(F("Initialisation done."));
// summary of inferencing settings (from model_metadata.h)
ei_printf("Inferencing settings:\n");
ei_printf("\tImage resolution: %dx%d\n", EI_CLASSIFIER_INPUT_WIDTH, EI_CLASSIFIER_INPUT_HEIGHT);
ei_printf("\tFrame size: %d\n", EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE);
ei_printf("\tNo. of classes: %d\n", sizeof(ei_classifier_inferencing_categories) / sizeof(ei_classifier_inferencing_categories[0]));
for (int i = 0; i < 15; i++) {
processImage(images[i], image_data);
ei::signal_t signal;
signal.total_length = EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT;
signal.get_data = &raw_feature_get_data;
ei_impulse_result_t result = { 0 };
EI_IMPULSE_ERROR ei_error = run_classifier(&signal, &result, debug_nn);
if (ei_error != EI_IMPULSE_OK) {
ei_printf("Failed to run impulse (%d)\n", ei_error);
break;
}
ei_printf("Predictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.): \n",
result.timing.dsp, result.timing.classification, result.timing.anomaly);
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
ei_printf(" %s: %.5f\n", result.classification[ix].label,
result.classification[ix].value);
}
delay(2000);
}
}
TfLiteStatus processImage(String filename, int8_t* image_data){
File jpegFile = SD.open(filename, FILE_READ);
if ( !jpegFile ) {
Serial.println(F("ERROR: File not found!"));
return kTfLiteError;
}
Serial.println(filename);
uint16_t imsize = jpegFile.size();
for (int i = 0; i < imsize; i++){
image_data[i] = (uint8_t)jpegFile.read();
}
return kTfLiteOk;
}
int raw_feature_get_data(size_t offset, size_t length, float *out_ptr) {
size_t pixel_ix = offset * 2;
size_t bytes_left = length;
size_t out_ptr_ix = 0;
// read byte for byte
while (bytes_left != 0) {
// grab the value and convert to r/g/b
uint16_t pixel = (image_data[pixel_ix] << 8) | image_data[pixel_ix+1];
uint8_t r, g, b;
r = ((pixel >> 11) & 0x1f) << 3;
g = ((pixel >> 5) & 0x3f) << 2;
b = (pixel & 0x1f) << 3;
// then convert to out_ptr format
float pixel_f = (r << 16) + (g << 8) + b;
out_ptr[out_ptr_ix] = pixel_f;
// and go to the next pixel
out_ptr_ix++;
pixel_ix+=2;
bytes_left--;
}
// and done!
return 0;
}
void loop()
{
}
Output:
Initialising SD card...
13:46:09.188 -> Initialisation done.
13:46:09.188 -> Inferencing settings:
13:46:09.188 -> Image resolution: 64x64
13:46:09.188 -> Frame size: 4096
13:46:09.188 -> No. of classes: 3
13:46:09.188 -> 1_B.JPG
13:46:09.985 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:09.985 -> Benign: 0.99609
13:46:09.985 -> Pre: 0.00000
13:46:09.985 -> Pro: 0.00000
13:46:12.131 -> 2_B.JPG
13:46:12.961 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:12.961 -> Benign: 0.99609
13:46:12.961 -> Pre: 0.00000
13:46:12.961 -> Pro: 0.00000
13:46:15.091 -> 3_B.JPG
13:46:15.925 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:15.925 -> Benign: 0.99609
13:46:15.925 -> Pre: 0.00000
13:46:15.925 -> Pro: 0.00000
13:46:18.060 -> 4_B.JPG
13:46:18.865 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:18.865 -> Benign: 0.99609
13:46:18.865 -> Pre: 0.00000
13:46:18.865 -> Pro: 0.00000
13:46:21.047 -> 5_B.JPG
13:46:21.883 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:21.883 -> Benign: 0.99609
13:46:21.883 -> Pre: 0.00000
13:46:21.883 -> Pro: 0.00000
13:46:23.985 -> 1_PRE.JPG
13:46:24.820 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:24.820 -> Benign: 0.99609
13:46:24.820 -> Pre: 0.00000
13:46:24.820 -> Pro: 0.00000
13:46:26.970 -> 2_PRE.JPG
13:46:27.788 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:27.788 -> Benign: 0.99609
13:46:27.788 -> Pre: 0.00000
13:46:27.788 -> Pro: 0.00000
13:46:29.934 -> 3_PRE.JPG
13:46:30.761 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:30.761 -> Benign: 0.99609
13:46:30.761 -> Pre: 0.00000
13:46:30.761 -> Pro: 0.00000
13:46:32.907 -> 4_PRE.JPG
13:46:33.728 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:33.728 -> Benign: 0.99609
13:46:33.728 -> Pre: 0.00000
13:46:33.728 -> Pro: 0.00000
13:46:35.901 -> 5_PRE.JPG
13:46:36.703 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:36.703 -> Benign: 0.99609
13:46:36.703 -> Pre: 0.00000
13:46:36.703 -> Pro: 0.00000
13:46:38.853 -> 1_PRO.JPG
13:46:39.658 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:39.658 -> Benign: 0.99609
13:46:39.658 -> Pre: 0.00000
13:46:39.658 -> Pro: 0.00000
13:46:41.799 -> 2_PRO.JPG
13:46:42.636 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:42.636 -> Benign: 0.99609
13:46:42.636 -> Pre: 0.00000
13:46:42.636 -> Pro: 0.00000
13:46:44.772 -> 3_PRO.JPG
13:46:45.588 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:45.588 -> Benign: 0.99609
13:46:45.588 -> Pre: 0.00000
13:46:45.588 -> Pro: 0.00000
13:46:47.763 -> 4_PRO.JPG
13:46:48.566 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:48.566 -> Benign: 0.99609
13:46:48.566 -> Pre: 0.00000
13:46:48.566 -> Pro: 0.00000
13:46:50.712 -> 5_PRO.JPG
13:46:51.564 -> Predictions (DSP: 5 ms., Classification: 730 ms., Anomaly: 0 ms.):
13:46:51.564 -> Benign: 0.99609
13:46:51.564 -> Pre: 0.00000
13:46:51.564 -> Pro: 0.00000
I feel I may not be reading the files correctly but have not been able to resolve this issue, any help would be appreciated ![]()
It looks like you are reading the files correctly but mess up on loading the image_data array. (don’t have an sd card to test with)
You can start by adding some debugging around here:
image_data[i] = (uint8_t)jpegFile.read();
Best
Eoin
Hi Eoin, thanks for the reply, could you elaborate, please?
Hi @AdamMiltonBarker ,
image_data is declared as a pointer not as an array that can hold image data. Also, the name image_data is used as a function parameter name.
I knew it would be something stupid. Sorry to waste your time and thank you both for your time.
