Skip to content

Commit 22fc50c

Browse files
author
AdamMiltonBarker
committed
Resolves #9 Inference Code and Documentation
1 parent 01df0b5 commit 22fc50c

File tree

7 files changed

+4909
-0
lines changed

7 files changed

+4909
-0
lines changed

arduino/all_nano_33_ble_sense/all_model.cpp

Lines changed: 4380 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
/* ALL Arduino Nano 33 BLE Sense Classifier
3+
4+
An experiment to explore how low powered microcontrollers, specifically the
5+
Arduino Nano 33 BLE Sense, can be used to detect Acute Lymphoblastic Leukemia.
6+
7+
MIT License
8+
9+
Copyright (c) 2021 Asociación de Investigacion en Inteligencia Artificial
10+
Para la Leucemia Peter Moss
11+
12+
Permission is hereby granted, free of charge, to any person obtaining a copy
13+
of this software and associated documentation files(the "Software"), to deal
14+
in the Software without restriction, including without limitation the rights
15+
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
16+
copies of the Software, and to permit persons to whom the Software is
17+
furnished to do so, subject to the following conditions:
18+
19+
The above copyright notice and this permission notice shall be included in all
20+
copies or substantial portions of the Software.
21+
22+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28+
SOFTWARE.
29+
30+
Contributors:
31+
- Adam Milton-Barker
32+
==============================================================================*/
33+
34+
#ifndef TENSORFLOW_LITE_MICRO_ACUTE_LYMPHOBLASTIC_LEUKEMIA_MODEL_DATA_H_
35+
#define TENSORFLOW_LITE_MICRO_ACUTE_LYMPHOBLASTIC_LEUKEMIA_MODEL_DATA_H_
36+
37+
extern const unsigned char all_model[];
38+
extern const int all_model_len;
39+
40+
#endif // TENSORFLOW_LITE_MICRO_ACUTE_LYMPHOBLASTIC_LEUKEMIA_MODEL_DATA_H_
Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
2+
/* ALL Arduino Nano 33 BLE Sense Classifier
3+
4+
An experiment to explore how low powered microcontrollers, specifically the
5+
Arduino Nano 33 BLE Sense, can be used to detect Acute Lymphoblastic Leukemia.
6+
7+
MIT License
8+
9+
Copyright (c) 2021 Asociación de Investigacion en Inteligencia Artificial
10+
Para la Leucemia Peter Moss
11+
12+
Permission is hereby granted, free of charge, to any person obtaining a copy
13+
of this software and associated documentation files(the "Software"), to deal
14+
in the Software without restriction, including without limitation the rights
15+
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
16+
copies of the Software, and to permit persons to whom the Software is
17+
furnished to do so, subject to the following conditions:
18+
19+
The above copyright notice and this permission notice shall be included in all
20+
copies or substantial portions of the Software.
21+
22+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28+
SOFTWARE.
29+
30+
Contributors:
31+
- Adam Milton-Barker
32+
==============================================================================*/
33+
34+
#include "Arduino.h"
35+
#include <SPI.h>
36+
37+
#include <TensorFlowLite.h>
38+
39+
#include "main_functions.h"
40+
#include "all_model.h"
41+
#include "model_settings.h"
42+
43+
#include "tensorflow/lite/micro/micro_error_reporter.h"
44+
#include "tensorflow/lite/micro/micro_interpreter.h"
45+
#include "tensorflow/lite/micro/micro_mutable_op_resolver.h"
46+
#include "tensorflow/lite/schema/schema_generated.h"
47+
#include "tensorflow/lite/version.h"
48+
49+
#include <JPEGDecoder.h>
50+
51+
String images[]={
52+
"Im006_1.jpg",
53+
"Im020_1.jpg",
54+
"Im024_1.jpg",
55+
"Im026_1.jpg",
56+
"Im028_1.jpg",
57+
"Im031_1.jpg",
58+
"Im035_0.jpg",
59+
"Im041_0.jpg",
60+
"Im047_0.jpg",
61+
"Im053_1.jpg",
62+
"Im057_1.jpg",
63+
"Im060_1.jpg",
64+
"Im063_1.jpg",
65+
"Im069_0.jpg",
66+
"Im074_0.jpg",
67+
"Im088_0.jpg",
68+
"Im095_0.jpg",
69+
"Im099_0.jpg",
70+
"Im101_0.jpg",
71+
"Im106_0.jpg"
72+
};
73+
74+
int tp = 0;
75+
int fp = 0;
76+
int tn = 0;
77+
int fn = 0;
78+
79+
namespace {
80+
tflite::ErrorReporter* error_reporter = nullptr;
81+
const tflite::Model* model = nullptr;
82+
tflite::MicroInterpreter* interpreter = nullptr;
83+
TfLiteTensor* input = nullptr;
84+
constexpr int kTensorArenaSize = 136 * 1024;
85+
static uint8_t tensor_arena[kTensorArenaSize];
86+
}
87+
88+
void setup() {
89+
90+
Serial.begin(9600);
91+
while (!Serial) {
92+
;
93+
}
94+
95+
Serial.println(F("Initialising SD card..."));
96+
if (!SD.begin(10)) {
97+
Serial.println(F("Initialisation failed!"));
98+
return;
99+
}
100+
Serial.println(F("Initialisation done."));
101+
102+
static tflite::MicroErrorReporter micro_error_reporter;
103+
error_reporter = &micro_error_reporter;
104+
105+
model = tflite::GetModel(all_model);
106+
if (model->version() != TFLITE_SCHEMA_VERSION) {
107+
TF_LITE_REPORT_ERROR(error_reporter,
108+
"Model provided is schema version %d not equal "
109+
"to supported version %d.",
110+
model->version(), TFLITE_SCHEMA_VERSION);
111+
return;
112+
}
113+
114+
static tflite::MicroMutableOpResolver<6> micro_op_resolver;
115+
micro_op_resolver.AddAveragePool2D();
116+
micro_op_resolver.AddConv2D();
117+
micro_op_resolver.AddDepthwiseConv2D();
118+
micro_op_resolver.AddReshape();
119+
micro_op_resolver.AddFullyConnected();
120+
micro_op_resolver.AddSoftmax();
121+
122+
static tflite::MicroInterpreter static_interpreter(
123+
model, micro_op_resolver, tensor_arena, kTensorArenaSize, error_reporter);
124+
interpreter = &static_interpreter;
125+
126+
TfLiteStatus allocate_status = interpreter->AllocateTensors();
127+
if (allocate_status != kTfLiteOk) {
128+
TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
129+
return;
130+
}
131+
132+
input = interpreter->input(0);
133+
getInputInfo(input);
134+
135+
for (int i = 0; i < 20; i++) {
136+
getImage(images[i], input->data.int8);
137+
TfLiteTensor* output = interpreter->output(0);
138+
int8_t all_score = output->data.int8[kAllIndex];
139+
int8_t no_all_score = output->data.int8[kNotAllIndex];
140+
processScores(all_score, no_all_score, images[i]);
141+
delay(2000);
142+
}
143+
144+
Serial.print("True Positives: ");
145+
Serial.println(tp);
146+
Serial.print("False Positives: ");
147+
Serial.println(fp);
148+
Serial.print("True Negatives: ");
149+
Serial.println(tn);
150+
Serial.print("False Negatives: ");
151+
Serial.println(fn);
152+
}
153+
154+
void getInputInfo(TfLiteTensor* input){
155+
Serial.println("");
156+
Serial.println("Model input info");
157+
Serial.println("===============");
158+
Serial.print("Dimensions: ");
159+
Serial.println(input->dims->size);
160+
Serial.print("Dim 1 size: ");
161+
Serial.println(input->dims->data[0]);
162+
Serial.print("Dim 2 size: ");
163+
Serial.println(input->dims->data[1]);
164+
Serial.print("Dim 3 size: ");
165+
Serial.println(input->dims->data[2]);
166+
Serial.print("Dim 4 size: ");
167+
Serial.println(input->dims->data[3]);
168+
Serial.print("Input type: ");
169+
Serial.println(input->type);
170+
Serial.println("===============");
171+
Serial.println("");
172+
}
173+
174+
TfLiteStatus getImage(String filepath, int8_t* image_data){
175+
File jpegFile = SD.open(filepath, FILE_READ);
176+
177+
if ( !jpegFile ) {
178+
Serial.print("ERROR: File not found!");
179+
return kTfLiteError;
180+
}
181+
182+
boolean decoded = JpegDec.decodeSdFile(jpegFile);
183+
processImage(filepath, image_data);
184+
185+
return kTfLiteOk;
186+
}
187+
188+
void processImage(String filename, int8_t* image_data){
189+
190+
// Crop the image by keeping a certain number of MCUs in each dimension
191+
const int keep_x_mcus = kNumCols / JpegDec.MCUWidth;
192+
const int keep_y_mcus = kNumRows / JpegDec.MCUHeight;
193+
194+
// Calculate how many MCUs we will throw away on the x axis
195+
const int skip_x_mcus = JpegDec.MCUSPerRow - keep_x_mcus;
196+
// Roughly center the crop by skipping half the throwaway MCUs at the
197+
// beginning of each row
198+
const int skip_start_x_mcus = skip_x_mcus / 2;
199+
// Index where we will start throwing away MCUs after the data
200+
const int skip_end_x_mcu_index = skip_start_x_mcus + keep_x_mcus;
201+
// Same approach for the columns
202+
const int skip_y_mcus = JpegDec.MCUSPerCol - keep_y_mcus;
203+
const int skip_start_y_mcus = skip_y_mcus / 2;
204+
const int skip_end_y_mcu_index = skip_start_y_mcus + keep_y_mcus;
205+
206+
// Pointer to the current pixel
207+
uint16_t* pImg;
208+
// Color of the current pixel
209+
uint16_t color;
210+
211+
// Loop over the MCUs
212+
while (JpegDec.read()) {
213+
// Skip over the initial set of rows
214+
if (JpegDec.MCUy < skip_start_y_mcus) {
215+
continue;
216+
}
217+
// Skip if we're on a column that we don't want
218+
if (JpegDec.MCUx < skip_start_x_mcus ||
219+
JpegDec.MCUx >= skip_end_x_mcu_index) {
220+
continue;
221+
}
222+
// Skip if we've got all the rows we want
223+
if (JpegDec.MCUy >= skip_end_y_mcu_index) {
224+
continue;
225+
}
226+
// Pointer to the current pixel
227+
pImg = JpegDec.pImage;
228+
229+
// The x and y indexes of the current MCU, ignoring the MCUs we skip
230+
int relative_mcu_x = JpegDec.MCUx - skip_start_x_mcus;
231+
int relative_mcu_y = JpegDec.MCUy - skip_start_y_mcus;
232+
233+
// The coordinates of the top left of this MCU when applied to the output
234+
// image
235+
int x_origin = relative_mcu_x * JpegDec.MCUWidth;
236+
int y_origin = relative_mcu_y * JpegDec.MCUHeight;
237+
238+
// Loop through the MCU's rows and columns
239+
for (int mcu_row = 0; mcu_row < JpegDec.MCUHeight; mcu_row++) {
240+
// The y coordinate of this pixel in the output index
241+
int current_y = y_origin + mcu_row;
242+
for (int mcu_col = 0; mcu_col < JpegDec.MCUWidth; mcu_col++) {
243+
// Read the color of the pixel as 16-bit integer
244+
color = *pImg++;
245+
// Extract the color values (5 red bits, 6 green, 5 blue)
246+
uint8_t r, g, b;
247+
r = ((color & 0xF800) >> 11) * 8;
248+
g = ((color & 0x07E0) >> 5) * 4;
249+
b = ((color & 0x001F) >> 0) * 8;
250+
// Convert to grayscale by calculating luminance
251+
// See https://en.wikipedia.org/wiki/Grayscale for magic numbers
252+
float gray_value = (0.2126 * r) + (0.7152 * g) + (0.0722 * b);
253+
254+
// Convert to signed 8-bit integer by subtracting 128.
255+
gray_value -= 128;
256+
// The x coordinate of this pixel in the output image
257+
int current_x = x_origin + mcu_col;
258+
// The index of this pixel in our flat output buffer
259+
int index = (current_y * kNumCols) + current_x;
260+
image_data[index] = static_cast<int8_t>(gray_value);
261+
}
262+
}
263+
}
264+
}
265+
266+
void processScores(int8_t all_score, int8_t no_all_score, String filename){
267+
268+
Serial.println(filename);
269+
Serial.println("===============");
270+
Serial.print("ALL positive score: ");
271+
Serial.println(all_score);
272+
Serial.print("ALL negative score: ");
273+
Serial.println(no_all_score);
274+
if(all_score > no_all_score && filename.indexOf("_1") > 0){
275+
Serial.println("True Positive");
276+
tp = tp + 1;
277+
}
278+
else if(all_score > no_all_score && filename.indexOf("_0") > 0){
279+
Serial.println("False Positive");
280+
fp = fp + 1;
281+
}
282+
else if(all_score < no_all_score && filename.indexOf("_1") > 0){
283+
Serial.println("False Negative");
284+
fn = fn + 1;
285+
}
286+
else if(all_score < no_all_score && filename.indexOf("_0") > 0){
287+
Serial.println("True Negative");
288+
tn = tn + 1;
289+
}
290+
Serial.println("");
291+
292+
static bool is_initialized = false;
293+
if (!is_initialized) {
294+
pinMode(LEDR, OUTPUT);
295+
pinMode(LEDG, OUTPUT);
296+
pinMode(LEDB, OUTPUT);
297+
is_initialized = true;
298+
}
299+
300+
digitalWrite(LEDG, HIGH);
301+
digitalWrite(LEDR, HIGH);
302+
303+
digitalWrite(LEDB, LOW);
304+
delay(100);
305+
digitalWrite(LEDB, HIGH);
306+
307+
if (all_score > no_all_score) {
308+
digitalWrite(LEDG, HIGH);
309+
digitalWrite(LEDR, LOW);
310+
delay(200);
311+
digitalWrite(LEDR, HIGH);
312+
} else {
313+
digitalWrite(LEDR, HIGH);
314+
digitalWrite(LEDG, LOW);
315+
delay(200);
316+
digitalWrite(LEDG, HIGH);
317+
}
318+
319+
}
320+
321+
void jpegInfo() {
322+
323+
Serial.println("JPEG image info");
324+
Serial.println("===============");
325+
Serial.print("Width :");
326+
Serial.println(JpegDec.width);
327+
Serial.print("Height :");
328+
Serial.println(JpegDec.height);
329+
Serial.print("Components :");
330+
Serial.println(JpegDec.comps);
331+
Serial.print("MCU / row :");
332+
Serial.println(JpegDec.MCUSPerRow);
333+
Serial.print("MCU / col :");
334+
Serial.println(JpegDec.MCUSPerCol);
335+
Serial.print("Scan type :");
336+
Serial.println(JpegDec.scanType);
337+
Serial.print("MCU width :");
338+
Serial.println(JpegDec.MCUWidth);
339+
Serial.print("MCU height :");
340+
Serial.println(JpegDec.MCUHeight);
341+
Serial.println("===============");
342+
Serial.println("");
343+
}
344+
345+
void loop() {
346+
}

0 commit comments

Comments
 (0)