chore: import local project into Gitea
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{"type": "library", "name": "TensorFlowLite_ESP32", "version": "1.0.0", "spec": {"owner": "tanakamasayuki", "id": 7394, "name": "TensorFlowLite_ESP32", "requirements": null, "uri": null}}
|
||||
@@ -0,0 +1,203 @@
|
||||
Copyright 2019 The TensorFlow Authors. All rights reserved.
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,138 @@
|
||||
# TensorFlowLite_ESP32
|
||||
|
||||
https://www.tensorflow.org/lite/microcontrollers/overview
|
||||
|
||||
https://github.com/espressif/tflite-micro-esp-examples
|
||||
|
||||
## Overview
|
||||
|
||||
This library runs TensorFlow machine learning models on microcontrollers, allowing you to build AI/ML applications powered by deep learning and neural networks.
|
||||
|
||||
With the included examples, you can recognize speech, detect people using a camera, and recognise "magic wand" gestures using an accelerometer.
|
||||
|
||||
The examples work best with the M5StickC(ESP32) board, which has a microphone and accelerometer.
|
||||
|
||||
## Examples
|
||||
|
||||
### hello_world
|
||||
|
||||
Outputs sine waves to serial outputs and build-in LEDs.
|
||||
|
||||
### micro_speech
|
||||
|
||||
This is a sample of speech recognition.
|
||||
The audio_provider and command_responder must be modified according to the environment in which they are used.
|
||||
|
||||
### person_detection
|
||||
|
||||
It is a person detection using a camera.
|
||||
The image_provider and detection_responder must be modified according to the environment in which they are used.
|
||||
|
||||
## OldExamples
|
||||
|
||||
This is a sample for older versions. It will not work as it is.
|
||||
|
||||
### magic_wand
|
||||
|
||||
This is gesture recognition using acceleration.
|
||||
The accelerometer_handler and output_handler must be modified according to the environment in which they are used.
|
||||
|
||||
### magic_wand_*
|
||||
|
||||
A sketch customized to look like a specific board.
|
||||
|
||||
- M5StickC
|
||||
- M5StackFire
|
||||
|
||||
### micro_speech_*
|
||||
|
||||
A sketch customized to look like a specific board.
|
||||
|
||||
- ESP-EYE
|
||||
- M5StickC
|
||||
- M5StackFire
|
||||
- ATOM Echo
|
||||
|
||||
### person_detection_ESP32-Camera
|
||||
|
||||
It is a person detection using a camera.
|
||||
This is a sample of using the ESP32 camera driver. Please configure the device you want to use in config.h.
|
||||
|
||||
#### Output sample
|
||||
```
|
||||
================================================
|
||||
==========================+=====================
|
||||
==================++-+++++++++=**--++++++++++++=
|
||||
===++++++++++++++++HH#-------=HH*-++++++++++++++
|
||||
+++++++++++++++---+HHH------+HH#-----#H+++++++++
|
||||
++++++++++++++-----HHH+---- HHHH----HHH-++++++++
|
||||
++++++++++++-------HHHH----HHH* ---HHH*-++++++++
|
||||
++++++++++---------HHHH ---***=---=*HH---+++++++
|
||||
++++++++++---------H***= +***---H*H* ----++++++
|
||||
++++++++++--------- ****=-***H ***H+------+++++
|
||||
++++++HHHH*+------- *************** -----#H#-+++
|
||||
+++++++ ###HH------**H**********H* --+HHHHHH*+=+
|
||||
+++++++++M##HH----+H*HHHHHHHHHHHHHHH#H#HHHHH*+=+
|
||||
+++++++++++#HHHHH=HHHHHHH#H#HHHHHHHH#H####H##+=+
|
||||
+++++++++++ HHHHHHHHHHHH####H##HH#HH=+++++++++=+
|
||||
++++++++*=+++#####################HH**********=+
|
||||
=++++++##M*++ ######H#############H-++++++++++++
|
||||
===++++#H*=M=++M#################H+++++++++++===
|
||||
======+++==M++++*###############H +++++++++++===
|
||||
======H*MHMH=+++++##############++++++++++++====
|
||||
==============+++=*###########H++++====*++==+==H
|
||||
==================###########H++++-+============
|
||||
*================+##########H###H=*+======*==+=*
|
||||
H**==============###########*===-HH+===+======**
|
||||
Person score:89 No person score:226
|
||||
```
|
||||
|
||||
```
|
||||
=======================+++======================
|
||||
====================+=+++++++++=================
|
||||
=================++++++++==++-----++++++++++++++
|
||||
=+++++++++++++++------*H*H#H#H=+----++++++++++++
|
||||
++++++++++++++------*H#HH##HHH*H-------+++++++++
|
||||
+++++++++++-------- ##HHHHHHH*H##-------++++++++
|
||||
++++++++++---------H#*H#HHHH#H*#H --------++++++
|
||||
+++++++++---------- #*HHH***H**+H ---------+++++
|
||||
+++++++++---------- H=***==***=++----------+++++
|
||||
+++++++++-----------==*******==*-------------+++
|
||||
+++++++++----------- -*HH**H*== --------++
|
||||
+++++++++------------H******** --HHHHHHH*+=+
|
||||
+++++++++------+=+++=*HHHHH**+#H*H=--#HHHHHHH+=+
|
||||
++++++++++=+========H##HH##HHH#H***--M#HHHH##+=+
|
||||
++++++++=*H#**======*HH#HHHH###H*H*H**++++++++=+
|
||||
+++++++=HHHHH**=====*HH#HHH*#HHH*HHHHHH******==+
|
||||
++++++HHHHH##******=*HHH#H*HHHHHHH#*###H++++++++
|
||||
=++++HHH####*#H*******H##H#HHHHHH##MH##HH+++++==
|
||||
===+HH#H##MMH**HH****HH#H#H##H##HHH*#H#H#H++++==
|
||||
==*HH#####MM#HHH*HHH####HHHH#####HHHH##*##=++===
|
||||
=HHH######MM##H#HH###MH*HH##H#####HHM##H##H=+==H
|
||||
==+M####MMM########M=H###HH#######H#MMH#####====
|
||||
====###MMM########H########MH##H###MMM######H+=*
|
||||
===##H#MMM#####H###M#HHHHHHH####HH#MMMMM####H-**
|
||||
Person score:251 No person score:42
|
||||
```
|
||||
|
||||
|
||||
### person_detection_*
|
||||
|
||||
A sketch customized to look like a specific board.
|
||||
|
||||
- M5CameraModelB
|
||||
- T-CameraV05
|
||||
|
||||
## How to make this library
|
||||
```
|
||||
cd scripts
|
||||
bash ./sync_from_tflite_micro.sh
|
||||
```
|
||||
|
||||
## special thanks
|
||||
|
||||
- https://www.tensorflow.org/lite/microcontrollers/overview
|
||||
- Arduino_TensorFlowLite librarie
|
||||
- https://github.com/adafruit/Adafruit_TFLite
|
||||
- https://github.com/boochow/TFLite_Micro_MagicWand_M5Stack
|
||||
- https://github.com/boochow/TFLite_Micro_MicroSpeech_M5Stack/tree/m5stickc
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "accelerometer_handler.h"
|
||||
|
||||
int begin_index = 0;
|
||||
|
||||
TfLiteStatus SetupAccelerometer(tflite::ErrorReporter* error_reporter) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
bool ReadAccelerometer(tflite::ErrorReporter* error_reporter, float* input,
|
||||
int length, bool reset_buffer) {
|
||||
begin_index += 3;
|
||||
// Reset begin_index to simulate behavior of loop buffer
|
||||
if (begin_index >= 600) begin_index = 0;
|
||||
// Only return true after the function was called 100 times, simulating the
|
||||
// desired behavior of a real implementation (which does not return data until
|
||||
// a sufficient amount is available)
|
||||
if (begin_index > 300) {
|
||||
for (int i = 0; i < length; ++i) input[i] = 0;
|
||||
return true;
|
||||
} else { return false; }
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_ACCELEROMETER_HANDLER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_ACCELEROMETER_HANDLER_H_
|
||||
|
||||
#define kChannelNumber 3
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
extern int begin_index;
|
||||
extern TfLiteStatus SetupAccelerometer(tflite::ErrorReporter* error_reporter);
|
||||
extern bool ReadAccelerometer(tflite::ErrorReporter* error_reporter,
|
||||
float* input, int length, bool reset_buffer);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_ACCELEROMETER_HANDLER_H_
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "main_functions.h"
|
||||
|
||||
// Arduino automatically calls the setup() and loop() functions in a sketch, so
|
||||
// where other systems need their own main routine in this file, it can be left
|
||||
// empty.
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
// The number of expected consecutive inferences for each gesture type.
|
||||
// These defaults were established with the SparkFun Edge board.
|
||||
const int kConsecutiveInferenceThresholds[3] = {15, 12, 10};
|
||||
@@ -0,0 +1,24 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_CONSTANTS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_CONSTANTS_H_
|
||||
|
||||
// The expected accelerometer data sample frequency
|
||||
const float kTargetHz = 25;
|
||||
|
||||
// The number of expected consecutive inferences for each gesture type
|
||||
extern const int kConsecutiveInferenceThresholds[3];
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_CONSTANTS_H_
|
||||
@@ -0,0 +1,55 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "gesture_predictor.h"
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
// How many times the most recent gesture has been matched in a row
|
||||
int continuous_count = 0;
|
||||
// The result of the last prediction
|
||||
int last_predict = -1;
|
||||
|
||||
// Return the result of the last prediction
|
||||
// 0: wing("W"), 1: ring("O"), 2: slope("angle"), 3: unknown
|
||||
int PredictGesture(float* output) {
|
||||
// Find whichever output has a probability > 0.8 (they sum to 1)
|
||||
int this_predict = -1;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (output[i] > 0.8) this_predict = i;
|
||||
}
|
||||
// No gesture was detected above the threshold
|
||||
if (this_predict == -1) {
|
||||
continuous_count = 0;
|
||||
last_predict = 3;
|
||||
return 3;
|
||||
}
|
||||
if (last_predict == this_predict) {
|
||||
continuous_count += 1;
|
||||
} else {
|
||||
continuous_count = 0;
|
||||
}
|
||||
last_predict = this_predict;
|
||||
// If we haven't yet had enough consecutive matches for this gesture,
|
||||
// report a negative result
|
||||
if (continuous_count < kConsecutiveInferenceThresholds[this_predict]) {
|
||||
return 3;
|
||||
}
|
||||
// Otherwise, we've seen a positive result, so clear all our variables
|
||||
// and report it
|
||||
continuous_count = 0;
|
||||
last_predict = -1;
|
||||
return this_predict;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_GESTURE_PREDICTOR_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_GESTURE_PREDICTOR_H_
|
||||
|
||||
extern int PredictGesture(float* output);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_GESTURE_PREDICTOR_H_
|
||||
@@ -0,0 +1,134 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include <TensorFlowLite_ESP32.h>
|
||||
|
||||
#include "main_functions.h"
|
||||
|
||||
#include "accelerometer_handler.h"
|
||||
#include "gesture_predictor.h"
|
||||
#include "magic_wand_model_data.h"
|
||||
#include "output_handler.h"
|
||||
#include "tensorflow/lite/experimental/micro/kernels/micro_ops.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_interpreter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_mutable_op_resolver.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
#include "tensorflow/lite/version.h"
|
||||
|
||||
// Globals, used for compatibility with Arduino-style sketches.
|
||||
namespace {
|
||||
tflite::ErrorReporter* error_reporter = nullptr;
|
||||
const tflite::Model* model = nullptr;
|
||||
tflite::MicroInterpreter* interpreter = nullptr;
|
||||
TfLiteTensor* model_input = nullptr;
|
||||
int input_length;
|
||||
|
||||
// Create an area of memory to use for input, output, and intermediate arrays.
|
||||
// The size of this will depend on the model you're using, and may need to be
|
||||
// determined by experimentation.
|
||||
constexpr int kTensorArenaSize = 60 * 1024;
|
||||
uint8_t tensor_arena[kTensorArenaSize];
|
||||
|
||||
// Whether we should clear the buffer next time we fetch data
|
||||
bool should_clear_buffer = false;
|
||||
} // namespace
|
||||
|
||||
// The name of this function is important for Arduino compatibility.
|
||||
void setup() {
|
||||
// Set up logging. Google style is to avoid globals or statics because of
|
||||
// lifetime uncertainty, but since this has a trivial destructor it's okay.
|
||||
static tflite::MicroErrorReporter micro_error_reporter; // NOLINT
|
||||
error_reporter = µ_error_reporter;
|
||||
|
||||
// Map the model into a usable data structure. This doesn't involve any
|
||||
// copying or parsing, it's a very lightweight operation.
|
||||
model = tflite::GetModel(g_magic_wand_model_data);
|
||||
if (model->version() != TFLITE_SCHEMA_VERSION) {
|
||||
error_reporter->Report(
|
||||
"Model provided is schema version %d not equal "
|
||||
"to supported version %d.",
|
||||
model->version(), TFLITE_SCHEMA_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pull in only the operation implementations we need.
|
||||
// This relies on a complete list of all the ops needed by this graph.
|
||||
// An easier approach is to just use the AllOpsResolver, but this will
|
||||
// incur some penalty in code space for op implementations that are not
|
||||
// needed by this graph.
|
||||
static tflite::MicroMutableOpResolver micro_mutable_op_resolver; // NOLINT
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_DEPTHWISE_CONV_2D,
|
||||
tflite::ops::micro::Register_DEPTHWISE_CONV_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_MAX_POOL_2D,
|
||||
tflite::ops::micro::Register_MAX_POOL_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(tflite::BuiltinOperator_CONV_2D,
|
||||
tflite::ops::micro::Register_CONV_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_FULLY_CONNECTED,
|
||||
tflite::ops::micro::Register_FULLY_CONNECTED());
|
||||
micro_mutable_op_resolver.AddBuiltin(tflite::BuiltinOperator_SOFTMAX,
|
||||
tflite::ops::micro::Register_SOFTMAX());
|
||||
|
||||
// Build an interpreter to run the model with
|
||||
static tflite::MicroInterpreter static_interpreter(
|
||||
model, micro_mutable_op_resolver, tensor_arena, kTensorArenaSize,
|
||||
error_reporter);
|
||||
interpreter = &static_interpreter;
|
||||
|
||||
// Allocate memory from the tensor_arena for the model's tensors
|
||||
interpreter->AllocateTensors();
|
||||
|
||||
// Obtain pointer to the model's input tensor
|
||||
model_input = interpreter->input(0);
|
||||
if ((model_input->dims->size != 4) || (model_input->dims->data[0] != 1) ||
|
||||
(model_input->dims->data[1] != 128) ||
|
||||
(model_input->dims->data[2] != kChannelNumber) ||
|
||||
(model_input->type != kTfLiteFloat32)) {
|
||||
error_reporter->Report("Bad input tensor parameters in model");
|
||||
return;
|
||||
}
|
||||
|
||||
input_length = model_input->bytes / sizeof(float);
|
||||
|
||||
TfLiteStatus setup_status = SetupAccelerometer(error_reporter);
|
||||
if (setup_status != kTfLiteOk) {
|
||||
error_reporter->Report("Set up failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Attempt to read new data from the accelerometer
|
||||
bool got_data = ReadAccelerometer(error_reporter, model_input->data.f,
|
||||
input_length, should_clear_buffer);
|
||||
// Don't try to clear the buffer again
|
||||
should_clear_buffer = false;
|
||||
// If there was no new data, wait until next time
|
||||
if (!got_data) return;
|
||||
// Run inference, and report any error
|
||||
TfLiteStatus invoke_status = interpreter->Invoke();
|
||||
if (invoke_status != kTfLiteOk) {
|
||||
error_reporter->Report("Invoke failed on index: %d\n", begin_index);
|
||||
return;
|
||||
}
|
||||
// Analyze the results to obtain a prediction
|
||||
int gesture_index = PredictGesture(interpreter->output(0)->data.f);
|
||||
// Clear the buffer next time we read data
|
||||
should_clear_buffer = gesture_index < 3;
|
||||
// Produce an output
|
||||
HandleOutput(error_reporter, gesture_index);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// This is a standard TensorFlow Lite model file that has been converted into a
|
||||
// C data array, so it can be easily compiled into a binary for devices that
|
||||
// don't have a file system. It was created using the command:
|
||||
// xxd -i magic_wand_model.tflite > magic_wand_model_data.cc
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAGIC_WAND_MODEL_DATA_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAGIC_WAND_MODEL_DATA_H_
|
||||
|
||||
extern const unsigned char g_magic_wand_model_data[];
|
||||
extern const int g_magic_wand_model_data_len;
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAGIC_WAND_MODEL_DATA_H_
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAIN_FUNCTIONS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAIN_FUNCTIONS_H_
|
||||
|
||||
// Initializes all data needed for the example. The name is important, and needs
|
||||
// to be setup() for Arduino compatibility.
|
||||
void setup();
|
||||
|
||||
// Runs one iteration of data gathering and inference. This should be called
|
||||
// repeatedly from the application code. The name needs to be loop() for Arduino
|
||||
// compatibility.
|
||||
void loop();
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAIN_FUNCTIONS_H_
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "output_handler.h"
|
||||
|
||||
void HandleOutput(tflite::ErrorReporter* error_reporter, int kind) {
|
||||
// light (red: wing, blue: ring, green: slope)
|
||||
if (kind == 0) {
|
||||
error_reporter->Report(
|
||||
"WING:\n\r* * *\n\r * * * "
|
||||
"*\n\r * * * *\n\r * * * *\n\r * * "
|
||||
"* *\n\r * *\n\r");
|
||||
} else if (kind == 1) {
|
||||
error_reporter->Report(
|
||||
"RING:\n\r *\n\r * *\n\r * *\n\r "
|
||||
" * *\n\r * *\n\r * *\n\r "
|
||||
" *\n\r");
|
||||
} else if (kind == 2) {
|
||||
error_reporter->Report(
|
||||
"SLOPE:\n\r *\n\r *\n\r *\n\r *\n\r "
|
||||
"*\n\r *\n\r *\n\r * * * * * * * *\n\r");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_OUTPUT_HANDLER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_OUTPUT_HANDLER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
void HandleOutput(tflite::ErrorReporter* error_reporter, int kind);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_OUTPUT_HANDLER_H_
|
||||
@@ -0,0 +1,94 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "accelerometer_handler.h"
|
||||
|
||||
#define M5STACK_MPU6886
|
||||
// #define M5STACK_MPU9250
|
||||
// #define M5STACK_MPU6050
|
||||
// #define M5STACK_200Q
|
||||
#include <M5Stack.h>
|
||||
|
||||
int begin_index = 0;
|
||||
|
||||
float save_data[600] = {0.0};
|
||||
bool pending_initial_data = true;
|
||||
long last_sample_millis = 0;
|
||||
|
||||
TfLiteStatus SetupAccelerometer(tflite::ErrorReporter* error_reporter) {
|
||||
M5.IMU.Init();
|
||||
error_reporter->Report("IMU Init");
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
static bool UpdateData() {
|
||||
bool new_data = false;
|
||||
if ((millis() - last_sample_millis) < 40) {
|
||||
return false;
|
||||
}
|
||||
last_sample_millis = millis();
|
||||
|
||||
float accX = 0.0F;
|
||||
float accY = 0.0F;
|
||||
float accZ = 0.0F;
|
||||
M5.IMU.getAccelData(&accX, &accY, &accZ);
|
||||
|
||||
/* this is a little annoying to figure out, as a tip - when
|
||||
holding the board straight, output should be (0, 0, 1)
|
||||
tiling the board 90* left, output should be (0, 1, 0)
|
||||
tilting the board 90* forward, output should be (1, 0, 0);
|
||||
*/
|
||||
save_data[begin_index++] = 1000 * accZ;
|
||||
save_data[begin_index++] = -1000 * accX;
|
||||
save_data[begin_index++] = 1000 * accY;
|
||||
|
||||
if (begin_index >= 600) {
|
||||
begin_index = 0;
|
||||
}
|
||||
new_data = true;
|
||||
|
||||
return new_data;
|
||||
}
|
||||
|
||||
bool ReadAccelerometer(tflite::ErrorReporter* error_reporter, float* input,
|
||||
int length, bool reset_buffer) {
|
||||
if (reset_buffer) {
|
||||
memset(save_data, 0, 600 * sizeof(float));
|
||||
begin_index = 0;
|
||||
pending_initial_data = true;
|
||||
}
|
||||
|
||||
if (!UpdateData()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pending_initial_data && begin_index >= 200) {
|
||||
pending_initial_data = false;
|
||||
}
|
||||
|
||||
if (pending_initial_data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; ++i) {
|
||||
int ring_array_index = begin_index + i - length;
|
||||
if (ring_array_index < 0) {
|
||||
ring_array_index += 600;
|
||||
}
|
||||
input[i] = save_data[ring_array_index];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_ACCELEROMETER_HANDLER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_ACCELEROMETER_HANDLER_H_
|
||||
|
||||
#define kChannelNumber 3
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
extern int begin_index;
|
||||
extern TfLiteStatus SetupAccelerometer(tflite::ErrorReporter* error_reporter);
|
||||
extern bool ReadAccelerometer(tflite::ErrorReporter* error_reporter,
|
||||
float* input, int length, bool reset_buffer);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_ACCELEROMETER_HANDLER_H_
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "main_functions.h"
|
||||
|
||||
// Arduino automatically calls the setup() and loop() functions in a sketch, so
|
||||
// where other systems need their own main routine in this file, it can be left
|
||||
// empty.
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
// The number of expected consecutive inferences for each gesture type.
|
||||
// These defaults were established with the SparkFun Edge board.
|
||||
const int kConsecutiveInferenceThresholds[3] = {5, 5, 5};
|
||||
@@ -0,0 +1,24 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_CONSTANTS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_CONSTANTS_H_
|
||||
|
||||
// The expected accelerometer data sample frequency
|
||||
const float kTargetHz = 25;
|
||||
|
||||
// The number of expected consecutive inferences for each gesture type
|
||||
extern const int kConsecutiveInferenceThresholds[3];
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_CONSTANTS_H_
|
||||
@@ -0,0 +1,55 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "gesture_predictor.h"
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
// How many times the most recent gesture has been matched in a row
|
||||
int continuous_count = 0;
|
||||
// The result of the last prediction
|
||||
int last_predict = -1;
|
||||
|
||||
// Return the result of the last prediction
|
||||
// 0: wing("W"), 1: ring("O"), 2: slope("angle"), 3: unknown
|
||||
int PredictGesture(float* output) {
|
||||
// Find whichever output has a probability > 0.8 (they sum to 1)
|
||||
int this_predict = -1;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (output[i] > 0.8) this_predict = i;
|
||||
}
|
||||
// No gesture was detected above the threshold
|
||||
if (this_predict == -1) {
|
||||
continuous_count = 0;
|
||||
last_predict = 3;
|
||||
return 3;
|
||||
}
|
||||
if (last_predict == this_predict) {
|
||||
continuous_count += 1;
|
||||
} else {
|
||||
continuous_count = 0;
|
||||
}
|
||||
last_predict = this_predict;
|
||||
// If we haven't yet had enough consecutive matches for this gesture,
|
||||
// report a negative result
|
||||
if (continuous_count < kConsecutiveInferenceThresholds[this_predict]) {
|
||||
return 3;
|
||||
}
|
||||
// Otherwise, we've seen a positive result, so clear all our variables
|
||||
// and report it
|
||||
continuous_count = 0;
|
||||
last_predict = -1;
|
||||
return this_predict;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_GESTURE_PREDICTOR_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_GESTURE_PREDICTOR_H_
|
||||
|
||||
extern int PredictGesture(float* output);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_GESTURE_PREDICTOR_H_
|
||||
@@ -0,0 +1,193 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include <TensorFlowLite_ESP32.h>
|
||||
|
||||
#include "main_functions.h"
|
||||
|
||||
#include "accelerometer_handler.h"
|
||||
#include "gesture_predictor.h"
|
||||
#include "magic_wand_model_data.h"
|
||||
#include "output_handler.h"
|
||||
#include "tensorflow/lite/experimental/micro/kernels/micro_ops.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_interpreter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_mutable_op_resolver.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
#include "tensorflow/lite/version.h"
|
||||
|
||||
#define M5STACK_MPU6886
|
||||
// #define M5STACK_MPU9250
|
||||
// #define M5STACK_MPU6050
|
||||
// #define M5STACK_200Q
|
||||
#include <M5Stack.h>
|
||||
|
||||
// Globals, used for compatibility with Arduino-style sketches.
|
||||
namespace {
|
||||
tflite::ErrorReporter* error_reporter = nullptr;
|
||||
const tflite::Model* model = nullptr;
|
||||
tflite::MicroInterpreter* interpreter = nullptr;
|
||||
TfLiteTensor* model_input = nullptr;
|
||||
int input_length;
|
||||
|
||||
// Create an area of memory to use for input, output, and intermediate arrays.
|
||||
// The size of this will depend on the model you're using, and may need to be
|
||||
// determined by experimentation.
|
||||
constexpr int kTensorArenaSize = 60 * 1024;
|
||||
uint8_t tensor_arena[kTensorArenaSize];
|
||||
|
||||
// Whether we should clear the buffer next time we fetch data
|
||||
bool should_clear_buffer = false;
|
||||
} // namespace
|
||||
|
||||
// The name of this function is important for Arduino compatibility.
|
||||
void setup() {
|
||||
M5.begin();
|
||||
M5.Power.begin();
|
||||
M5.Lcd.fillScreen(BLACK);
|
||||
M5.Lcd.setCursor(0, 0);
|
||||
M5.Lcd.setTextFont(2);
|
||||
M5.Lcd.setTextColor(YELLOW);
|
||||
M5.Lcd.printf("Magic Wand\n");
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
M5.Lcd.setTextFont(1);
|
||||
M5.Lcd.setTextSize(2);
|
||||
|
||||
// Set up logging. Google style is to avoid globals or statics because of
|
||||
// lifetime uncertainty, but since this has a trivial destructor it's okay.
|
||||
static tflite::MicroErrorReporter micro_error_reporter; // NOLINT
|
||||
error_reporter = µ_error_reporter;
|
||||
|
||||
// Map the model into a usable data structure. This doesn't involve any
|
||||
// copying or parsing, it's a very lightweight operation.
|
||||
model = tflite::GetModel(g_magic_wand_model_data);
|
||||
if (model->version() != TFLITE_SCHEMA_VERSION) {
|
||||
error_reporter->Report(
|
||||
"Model provided is schema version %d not equal "
|
||||
"to supported version %d.",
|
||||
model->version(), TFLITE_SCHEMA_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pull in only the operation implementations we need.
|
||||
// This relies on a complete list of all the ops needed by this graph.
|
||||
// An easier approach is to just use the AllOpsResolver, but this will
|
||||
// incur some penalty in code space for op implementations that are not
|
||||
// needed by this graph.
|
||||
static tflite::MicroMutableOpResolver micro_mutable_op_resolver; // NOLINT
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_DEPTHWISE_CONV_2D,
|
||||
tflite::ops::micro::Register_DEPTHWISE_CONV_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_MAX_POOL_2D,
|
||||
tflite::ops::micro::Register_MAX_POOL_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(tflite::BuiltinOperator_CONV_2D,
|
||||
tflite::ops::micro::Register_CONV_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_FULLY_CONNECTED,
|
||||
tflite::ops::micro::Register_FULLY_CONNECTED());
|
||||
micro_mutable_op_resolver.AddBuiltin(tflite::BuiltinOperator_SOFTMAX,
|
||||
tflite::ops::micro::Register_SOFTMAX());
|
||||
|
||||
// Build an interpreter to run the model with
|
||||
static tflite::MicroInterpreter static_interpreter(
|
||||
model, micro_mutable_op_resolver, tensor_arena, kTensorArenaSize,
|
||||
error_reporter);
|
||||
interpreter = &static_interpreter;
|
||||
|
||||
// Allocate memory from the tensor_arena for the model's tensors
|
||||
interpreter->AllocateTensors();
|
||||
|
||||
// Obtain pointer to the model's input tensor
|
||||
model_input = interpreter->input(0);
|
||||
if ((model_input->dims->size != 4) || (model_input->dims->data[0] != 1) ||
|
||||
(model_input->dims->data[1] != 128) ||
|
||||
(model_input->dims->data[2] != kChannelNumber) ||
|
||||
(model_input->type != kTfLiteFloat32)) {
|
||||
error_reporter->Report("Bad input tensor parameters in model");
|
||||
return;
|
||||
}
|
||||
|
||||
input_length = model_input->bytes / sizeof(float);
|
||||
|
||||
TfLiteStatus setup_status = SetupAccelerometer(error_reporter);
|
||||
if (setup_status != kTfLiteOk) {
|
||||
error_reporter->Report("Set up failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Attempt to read new data from the accelerometer
|
||||
bool got_data = ReadAccelerometer(error_reporter, model_input->data.f,
|
||||
input_length, should_clear_buffer);
|
||||
// Don't try to clear the buffer again
|
||||
should_clear_buffer = false;
|
||||
// If there was no new data, wait until next time
|
||||
if (!got_data) return;
|
||||
// Run inference, and report any error
|
||||
TfLiteStatus invoke_status = interpreter->Invoke();
|
||||
if (invoke_status != kTfLiteOk) {
|
||||
error_reporter->Report("Invoke failed on index: %d\n", begin_index);
|
||||
return;
|
||||
}
|
||||
|
||||
char s[64];
|
||||
float *f = model_input->data.f;
|
||||
float *p = interpreter->output(0)->data.f;
|
||||
sprintf(s, "%+6.0f : %+6.0f : %+6.0f || W %3.2f : R %3.2f : S %3.2f", \
|
||||
f[381], f[382], f[383], p[0], p[1], p[2]);
|
||||
error_reporter->Report(s);
|
||||
|
||||
M5.Lcd.setCursor(0, 32);
|
||||
M5.Lcd.setTextColor(ORANGE, BLACK);
|
||||
M5.Lcd.printf("INPUT \n");
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
M5.Lcd.printf("X%5.0f\n", f[381]);
|
||||
M5.Lcd.printf("Y%5.0f\n", f[382]);
|
||||
M5.Lcd.printf("Z%5.0f\n", f[383]);
|
||||
M5.Lcd.setTextColor(ORANGE, BLACK);
|
||||
M5.Lcd.printf("OUTPUT\n");
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
M5.Lcd.printf("W%5.2f\n", p[0]);
|
||||
M5.Lcd.printf("R%5.2f\n", p[1]);
|
||||
M5.Lcd.printf("S%5.2f\n", p[2]);
|
||||
|
||||
|
||||
// Analyze the results to obtain a prediction
|
||||
int gesture_index = PredictGesture(interpreter->output(0)->data.f);
|
||||
// Clear the buffer next time we read data
|
||||
should_clear_buffer = gesture_index < 3;
|
||||
// Produce an output
|
||||
HandleOutput(error_reporter, gesture_index);
|
||||
|
||||
if (gesture_index < 3) {
|
||||
M5.Lcd.setCursor(20, 60);
|
||||
M5.Lcd.setTextFont(1);
|
||||
M5.Lcd.setTextSize(7);
|
||||
if (gesture_index == 0) {
|
||||
M5.Lcd.setTextColor(RED, BLACK);
|
||||
M5.Lcd.print("W");
|
||||
} else if (gesture_index == 1) {
|
||||
M5.Lcd.setTextColor(BLUE, BLACK);
|
||||
M5.Lcd.print("R");
|
||||
} else if (gesture_index == 2) {
|
||||
M5.Lcd.setTextColor(GREEN, BLACK);
|
||||
M5.Lcd.print("S");
|
||||
}
|
||||
M5.Lcd.setTextSize(2);
|
||||
M5.Lcd.setTextFont(1);
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// This is a standard TensorFlow Lite model file that has been converted into a
|
||||
// C data array, so it can be easily compiled into a binary for devices that
|
||||
// don't have a file system. It was created using the command:
|
||||
// xxd -i magic_wand_model.tflite > magic_wand_model_data.cc
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAGIC_WAND_MODEL_DATA_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAGIC_WAND_MODEL_DATA_H_
|
||||
|
||||
extern const unsigned char g_magic_wand_model_data[];
|
||||
extern const int g_magic_wand_model_data_len;
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAGIC_WAND_MODEL_DATA_H_
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAIN_FUNCTIONS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAIN_FUNCTIONS_H_
|
||||
|
||||
// Initializes all data needed for the example. The name is important, and needs
|
||||
// to be setup() for Arduino compatibility.
|
||||
void setup();
|
||||
|
||||
// Runs one iteration of data gathering and inference. This should be called
|
||||
// repeatedly from the application code. The name needs to be loop() for Arduino
|
||||
// compatibility.
|
||||
void loop();
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAIN_FUNCTIONS_H_
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "output_handler.h"
|
||||
|
||||
void HandleOutput(tflite::ErrorReporter* error_reporter, int kind) {
|
||||
// light (red: wing, blue: ring, green: slope)
|
||||
if (kind == 0) {
|
||||
error_reporter->Report(
|
||||
"WING:\n\r* * *\n\r * * * "
|
||||
"*\n\r * * * *\n\r * * * *\n\r * * "
|
||||
"* *\n\r * *\n\r");
|
||||
} else if (kind == 1) {
|
||||
error_reporter->Report(
|
||||
"RING:\n\r *\n\r * *\n\r * *\n\r "
|
||||
" * *\n\r * *\n\r * *\n\r "
|
||||
" *\n\r");
|
||||
} else if (kind == 2) {
|
||||
error_reporter->Report(
|
||||
"SLOPE:\n\r *\n\r *\n\r *\n\r *\n\r "
|
||||
"*\n\r *\n\r *\n\r * * * * * * * *\n\r");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_OUTPUT_HANDLER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_OUTPUT_HANDLER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
void HandleOutput(tflite::ErrorReporter* error_reporter, int kind);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_OUTPUT_HANDLER_H_
|
||||
@@ -0,0 +1,84 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "accelerometer_handler.h"
|
||||
#include <M5StickC.h>
|
||||
|
||||
int begin_index = 0;
|
||||
|
||||
float save_data[600] = {0.0};
|
||||
bool pending_initial_data = true;
|
||||
long last_sample_millis = 0;
|
||||
|
||||
TfLiteStatus SetupAccelerometer(tflite::ErrorReporter* error_reporter) {
|
||||
M5.IMU.Init();
|
||||
error_reporter->Report("IMU Init");
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
static bool UpdateData() {
|
||||
bool new_data = false;
|
||||
if ((millis() - last_sample_millis) < 40) {
|
||||
return false;
|
||||
}
|
||||
last_sample_millis = millis();
|
||||
|
||||
float accX = 0.0F;
|
||||
float accY = 0.0F;
|
||||
float accZ = 0.0F;
|
||||
M5.IMU.getAccelData(&accX, &accY, &accZ);
|
||||
|
||||
save_data[begin_index++] = 1000 * accZ;
|
||||
save_data[begin_index++] = 1000 * accX;
|
||||
save_data[begin_index++] = 1000 * accY;
|
||||
|
||||
if (begin_index >= 600) {
|
||||
begin_index = 0;
|
||||
}
|
||||
new_data = true;
|
||||
|
||||
return new_data;
|
||||
}
|
||||
|
||||
bool ReadAccelerometer(tflite::ErrorReporter* error_reporter, float* input,
|
||||
int length, bool reset_buffer) {
|
||||
if (reset_buffer) {
|
||||
memset(save_data, 0, 600 * sizeof(float));
|
||||
begin_index = 0;
|
||||
pending_initial_data = true;
|
||||
}
|
||||
|
||||
if (!UpdateData()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pending_initial_data && begin_index >= 200) {
|
||||
pending_initial_data = false;
|
||||
}
|
||||
|
||||
if (pending_initial_data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; ++i) {
|
||||
int ring_array_index = begin_index + i - length;
|
||||
if (ring_array_index < 0) {
|
||||
ring_array_index += 600;
|
||||
}
|
||||
input[i] = save_data[ring_array_index];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_ACCELEROMETER_HANDLER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_ACCELEROMETER_HANDLER_H_
|
||||
|
||||
#define kChannelNumber 3
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
extern int begin_index;
|
||||
extern TfLiteStatus SetupAccelerometer(tflite::ErrorReporter* error_reporter);
|
||||
extern bool ReadAccelerometer(tflite::ErrorReporter* error_reporter,
|
||||
float* input, int length, bool reset_buffer);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_ACCELEROMETER_HANDLER_H_
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "main_functions.h"
|
||||
|
||||
// Arduino automatically calls the setup() and loop() functions in a sketch, so
|
||||
// where other systems need their own main routine in this file, it can be left
|
||||
// empty.
|
||||
@@ -0,0 +1,20 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
// The number of expected consecutive inferences for each gesture type.
|
||||
// These defaults were established with the SparkFun Edge board.
|
||||
const int kConsecutiveInferenceThresholds[3] = {5, 5, 5};
|
||||
@@ -0,0 +1,24 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_CONSTANTS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_CONSTANTS_H_
|
||||
|
||||
// The expected accelerometer data sample frequency
|
||||
const float kTargetHz = 25;
|
||||
|
||||
// The number of expected consecutive inferences for each gesture type
|
||||
extern const int kConsecutiveInferenceThresholds[3];
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_CONSTANTS_H_
|
||||
@@ -0,0 +1,55 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "gesture_predictor.h"
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
// How many times the most recent gesture has been matched in a row
|
||||
int continuous_count = 0;
|
||||
// The result of the last prediction
|
||||
int last_predict = -1;
|
||||
|
||||
// Return the result of the last prediction
|
||||
// 0: wing("W"), 1: ring("O"), 2: slope("angle"), 3: unknown
|
||||
int PredictGesture(float* output) {
|
||||
// Find whichever output has a probability > 0.8 (they sum to 1)
|
||||
int this_predict = -1;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (output[i] > 0.8) this_predict = i;
|
||||
}
|
||||
// No gesture was detected above the threshold
|
||||
if (this_predict == -1) {
|
||||
continuous_count = 0;
|
||||
last_predict = 3;
|
||||
return 3;
|
||||
}
|
||||
if (last_predict == this_predict) {
|
||||
continuous_count += 1;
|
||||
} else {
|
||||
continuous_count = 0;
|
||||
}
|
||||
last_predict = this_predict;
|
||||
// If we haven't yet had enough consecutive matches for this gesture,
|
||||
// report a negative result
|
||||
if (continuous_count < kConsecutiveInferenceThresholds[this_predict]) {
|
||||
return 3;
|
||||
}
|
||||
// Otherwise, we've seen a positive result, so clear all our variables
|
||||
// and report it
|
||||
continuous_count = 0;
|
||||
last_predict = -1;
|
||||
return this_predict;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_GESTURE_PREDICTOR_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_GESTURE_PREDICTOR_H_
|
||||
|
||||
extern int PredictGesture(float* output);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_GESTURE_PREDICTOR_H_
|
||||
@@ -0,0 +1,188 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include <TensorFlowLite_ESP32.h>
|
||||
|
||||
#include "main_functions.h"
|
||||
|
||||
#include "accelerometer_handler.h"
|
||||
#include "gesture_predictor.h"
|
||||
#include "magic_wand_model_data.h"
|
||||
#include "output_handler.h"
|
||||
#include "tensorflow/lite/experimental/micro/kernels/micro_ops.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_interpreter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_mutable_op_resolver.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
#include "tensorflow/lite/version.h"
|
||||
|
||||
#include <M5StickC.h>
|
||||
|
||||
// Globals, used for compatibility with Arduino-style sketches.
|
||||
namespace {
|
||||
tflite::ErrorReporter* error_reporter = nullptr;
|
||||
const tflite::Model* model = nullptr;
|
||||
tflite::MicroInterpreter* interpreter = nullptr;
|
||||
TfLiteTensor* model_input = nullptr;
|
||||
int input_length;
|
||||
|
||||
// Create an area of memory to use for input, output, and intermediate arrays.
|
||||
// The size of this will depend on the model you're using, and may need to be
|
||||
// determined by experimentation.
|
||||
constexpr int kTensorArenaSize = 60 * 1024;
|
||||
uint8_t tensor_arena[kTensorArenaSize];
|
||||
|
||||
// Whether we should clear the buffer next time we fetch data
|
||||
bool should_clear_buffer = false;
|
||||
} // namespace
|
||||
|
||||
// The name of this function is important for Arduino compatibility.
|
||||
void setup() {
|
||||
M5.begin();
|
||||
M5.Lcd.fillScreen(BLACK);
|
||||
M5.Lcd.setCursor(0, 0);
|
||||
M5.Lcd.setTextFont(2);
|
||||
M5.Lcd.setTextColor(YELLOW);
|
||||
M5.Lcd.printf("Magic Wand\n");
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
M5.Lcd.setTextFont(1);
|
||||
M5.Lcd.setTextSize(2);
|
||||
|
||||
// Set up logging. Google style is to avoid globals or statics because of
|
||||
// lifetime uncertainty, but since this has a trivial destructor it's okay.
|
||||
static tflite::MicroErrorReporter micro_error_reporter; // NOLINT
|
||||
error_reporter = µ_error_reporter;
|
||||
|
||||
// Map the model into a usable data structure. This doesn't involve any
|
||||
// copying or parsing, it's a very lightweight operation.
|
||||
model = tflite::GetModel(g_magic_wand_model_data);
|
||||
if (model->version() != TFLITE_SCHEMA_VERSION) {
|
||||
error_reporter->Report(
|
||||
"Model provided is schema version %d not equal "
|
||||
"to supported version %d.",
|
||||
model->version(), TFLITE_SCHEMA_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pull in only the operation implementations we need.
|
||||
// This relies on a complete list of all the ops needed by this graph.
|
||||
// An easier approach is to just use the AllOpsResolver, but this will
|
||||
// incur some penalty in code space for op implementations that are not
|
||||
// needed by this graph.
|
||||
static tflite::MicroMutableOpResolver micro_mutable_op_resolver; // NOLINT
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_DEPTHWISE_CONV_2D,
|
||||
tflite::ops::micro::Register_DEPTHWISE_CONV_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_MAX_POOL_2D,
|
||||
tflite::ops::micro::Register_MAX_POOL_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(tflite::BuiltinOperator_CONV_2D,
|
||||
tflite::ops::micro::Register_CONV_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_FULLY_CONNECTED,
|
||||
tflite::ops::micro::Register_FULLY_CONNECTED());
|
||||
micro_mutable_op_resolver.AddBuiltin(tflite::BuiltinOperator_SOFTMAX,
|
||||
tflite::ops::micro::Register_SOFTMAX());
|
||||
|
||||
// Build an interpreter to run the model with
|
||||
static tflite::MicroInterpreter static_interpreter(
|
||||
model, micro_mutable_op_resolver, tensor_arena, kTensorArenaSize,
|
||||
error_reporter);
|
||||
interpreter = &static_interpreter;
|
||||
|
||||
// Allocate memory from the tensor_arena for the model's tensors
|
||||
interpreter->AllocateTensors();
|
||||
|
||||
// Obtain pointer to the model's input tensor
|
||||
model_input = interpreter->input(0);
|
||||
if ((model_input->dims->size != 4) || (model_input->dims->data[0] != 1) ||
|
||||
(model_input->dims->data[1] != 128) ||
|
||||
(model_input->dims->data[2] != kChannelNumber) ||
|
||||
(model_input->type != kTfLiteFloat32)) {
|
||||
error_reporter->Report("Bad input tensor parameters in model");
|
||||
return;
|
||||
}
|
||||
|
||||
input_length = model_input->bytes / sizeof(float);
|
||||
|
||||
TfLiteStatus setup_status = SetupAccelerometer(error_reporter);
|
||||
if (setup_status != kTfLiteOk) {
|
||||
error_reporter->Report("Set up failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Attempt to read new data from the accelerometer
|
||||
bool got_data = ReadAccelerometer(error_reporter, model_input->data.f,
|
||||
input_length, should_clear_buffer);
|
||||
// Don't try to clear the buffer again
|
||||
should_clear_buffer = false;
|
||||
// If there was no new data, wait until next time
|
||||
if (!got_data) return;
|
||||
// Run inference, and report any error
|
||||
TfLiteStatus invoke_status = interpreter->Invoke();
|
||||
if (invoke_status != kTfLiteOk) {
|
||||
error_reporter->Report("Invoke failed on index: %d\n", begin_index);
|
||||
return;
|
||||
}
|
||||
|
||||
char s[64];
|
||||
float *f = model_input->data.f;
|
||||
float *p = interpreter->output(0)->data.f;
|
||||
sprintf(s, "%+6.0f : %+6.0f : %+6.0f || W %3.2f : R %3.2f : S %3.2f", \
|
||||
f[381], f[382], f[383], p[0], p[1], p[2]);
|
||||
error_reporter->Report(s);
|
||||
|
||||
M5.Lcd.setCursor(0, 32);
|
||||
M5.Lcd.setTextColor(ORANGE, BLACK);
|
||||
M5.Lcd.printf("INPUT \n");
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
M5.Lcd.printf("X%5.0f\n", f[381]);
|
||||
M5.Lcd.printf("Y%5.0f\n", f[382]);
|
||||
M5.Lcd.printf("Z%5.0f\n", f[383]);
|
||||
M5.Lcd.setTextColor(ORANGE, BLACK);
|
||||
M5.Lcd.printf("OUTPUT\n");
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
M5.Lcd.printf("W%5.2f\n", p[0]);
|
||||
M5.Lcd.printf("R%5.2f\n", p[1]);
|
||||
M5.Lcd.printf("S%5.2f\n", p[2]);
|
||||
|
||||
|
||||
// Analyze the results to obtain a prediction
|
||||
int gesture_index = PredictGesture(interpreter->output(0)->data.f);
|
||||
// Clear the buffer next time we read data
|
||||
should_clear_buffer = gesture_index < 3;
|
||||
// Produce an output
|
||||
HandleOutput(error_reporter, gesture_index);
|
||||
|
||||
if (gesture_index < 3) {
|
||||
M5.Lcd.setCursor(20, 60);
|
||||
M5.Lcd.setTextFont(1);
|
||||
M5.Lcd.setTextSize(7);
|
||||
if (gesture_index == 0) {
|
||||
M5.Lcd.setTextColor(RED, BLACK);
|
||||
M5.Lcd.print("W");
|
||||
} else if (gesture_index == 1) {
|
||||
M5.Lcd.setTextColor(BLUE, BLACK);
|
||||
M5.Lcd.print("R");
|
||||
} else if (gesture_index == 2) {
|
||||
M5.Lcd.setTextColor(GREEN, BLACK);
|
||||
M5.Lcd.print("S");
|
||||
}
|
||||
M5.Lcd.setTextSize(2);
|
||||
M5.Lcd.setTextFont(1);
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// This is a standard TensorFlow Lite model file that has been converted into a
|
||||
// C data array, so it can be easily compiled into a binary for devices that
|
||||
// don't have a file system. It was created using the command:
|
||||
// xxd -i magic_wand_model.tflite > magic_wand_model_data.cc
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAGIC_WAND_MODEL_DATA_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAGIC_WAND_MODEL_DATA_H_
|
||||
|
||||
extern const unsigned char g_magic_wand_model_data[];
|
||||
extern const int g_magic_wand_model_data_len;
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAGIC_WAND_MODEL_DATA_H_
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAIN_FUNCTIONS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAIN_FUNCTIONS_H_
|
||||
|
||||
// Initializes all data needed for the example. The name is important, and needs
|
||||
// to be setup() for Arduino compatibility.
|
||||
void setup();
|
||||
|
||||
// Runs one iteration of data gathering and inference. This should be called
|
||||
// repeatedly from the application code. The name needs to be loop() for Arduino
|
||||
// compatibility.
|
||||
void loop();
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_MAIN_FUNCTIONS_H_
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "output_handler.h"
|
||||
|
||||
void HandleOutput(tflite::ErrorReporter* error_reporter, int kind) {
|
||||
// light (red: wing, blue: ring, green: slope)
|
||||
if (kind == 0) {
|
||||
error_reporter->Report(
|
||||
"WING:\n\r* * *\n\r * * * "
|
||||
"*\n\r * * * *\n\r * * * *\n\r * * "
|
||||
"* *\n\r * *\n\r");
|
||||
} else if (kind == 1) {
|
||||
error_reporter->Report(
|
||||
"RING:\n\r *\n\r * *\n\r * *\n\r "
|
||||
" * *\n\r * *\n\r * *\n\r "
|
||||
" *\n\r");
|
||||
} else if (kind == 2) {
|
||||
error_reporter->Report(
|
||||
"SLOPE:\n\r *\n\r *\n\r *\n\r *\n\r "
|
||||
"*\n\r *\n\r *\n\r * * * * * * * *\n\r");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_OUTPUT_HANDLER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_OUTPUT_HANDLER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
void HandleOutput(tflite::ErrorReporter* error_reporter, int kind);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MAGIC_WAND_OUTPUT_HANDLER_H_
|
||||
@@ -0,0 +1,176 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "audio_provider.h"
|
||||
#include "micro_model_settings.h"
|
||||
#include <Arduino.h>
|
||||
#include <M5Atom.h>
|
||||
#include <driver/i2s.h>
|
||||
|
||||
#define I2S_NUM I2S_NUM_0 // 0 or 1
|
||||
#define I2S_SAMPLE_RATE 16000
|
||||
|
||||
#define I2S_PIN_CLK I2S_PIN_NO_CHANGE
|
||||
#define I2S_PIN_WS 33
|
||||
#define I2S_PIN_DOUT I2S_PIN_NO_CHANGE
|
||||
#define I2S_PIN_DIN 23
|
||||
|
||||
#define BUFFER_SIZE 512
|
||||
|
||||
void CaptureSamples();
|
||||
extern QueueHandle_t xQueueAudioWave;
|
||||
|
||||
namespace {
|
||||
bool g_is_audio_initialized = false;
|
||||
// An internal buffer able to fit 16x our sample size
|
||||
constexpr int kAudioCaptureBufferSize = BUFFER_SIZE * 16;
|
||||
int16_t g_audio_capture_buffer[kAudioCaptureBufferSize];
|
||||
// A buffer that holds our output
|
||||
int16_t g_audio_output_buffer[kMaxAudioSampleSize];
|
||||
// Mark as volatile so we can check in a while loop to see if
|
||||
// any samples have arrived yet.
|
||||
volatile int32_t g_latest_audio_timestamp = 0;
|
||||
// Our callback buffer for collecting a chunk of data
|
||||
volatile int16_t recording_buffer[BUFFER_SIZE];
|
||||
} // namespace
|
||||
|
||||
void InitI2S()
|
||||
{
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
|
||||
.sample_rate = I2S_SAMPLE_RATE,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_ALL_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_I2S,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.dma_buf_count = 4,
|
||||
.dma_buf_len = 256,
|
||||
.use_apll = false,
|
||||
.tx_desc_auto_clear = false,
|
||||
.fixed_mclk = 0
|
||||
};
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = I2S_PIN_CLK,
|
||||
.ws_io_num = I2S_PIN_WS,
|
||||
.data_out_num = I2S_PIN_DOUT,
|
||||
.data_in_num = I2S_PIN_DIN,
|
||||
};
|
||||
|
||||
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
|
||||
i2s_set_pin(I2S_NUM, &pin_config);
|
||||
}
|
||||
|
||||
void AudioRecordingTask(void *pvParameters) {
|
||||
static uint16_t audio_idx = 0;
|
||||
size_t bytes_read;
|
||||
int16_t i2s_data[2];
|
||||
int16_t sample;
|
||||
|
||||
while (1) {
|
||||
|
||||
if (audio_idx >= BUFFER_SIZE) {
|
||||
xQueueSend(xQueueAudioWave, &sample, 0);
|
||||
CaptureSamples();
|
||||
audio_idx = 0;
|
||||
}
|
||||
|
||||
i2s_read(I2S_NUM_0, &i2s_data, 4, &bytes_read, portMAX_DELAY );
|
||||
|
||||
if (bytes_read > 0) {
|
||||
sample = i2s_data[0];
|
||||
recording_buffer[audio_idx] = sample;
|
||||
audio_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureSamples() {
|
||||
// This is how many bytes of new data we have each time this is called
|
||||
const int number_of_samples = BUFFER_SIZE;
|
||||
// Calculate what timestamp the last audio sample represents
|
||||
const int32_t time_in_ms =
|
||||
g_latest_audio_timestamp +
|
||||
(number_of_samples / (kAudioSampleFrequency / 1000));
|
||||
// Determine the index, in the history of all samples, of the last sample
|
||||
const int32_t start_sample_offset =
|
||||
g_latest_audio_timestamp * (kAudioSampleFrequency / 1000);
|
||||
// Determine the index of this sample in our ring buffer
|
||||
const int capture_index = start_sample_offset % kAudioCaptureBufferSize;
|
||||
// Read the data to the correct place in our buffer, note 2 bytes per buffer entry
|
||||
memcpy(g_audio_capture_buffer + capture_index, (void *)recording_buffer, BUFFER_SIZE * 2);
|
||||
// This is how we let the outside world know that new audio data has arrived.
|
||||
g_latest_audio_timestamp = time_in_ms;
|
||||
|
||||
//int peak = (max_audio - min_audio);
|
||||
//Serial.printf("peak-to-peak: %6d\n", peak);
|
||||
}
|
||||
|
||||
TfLiteStatus InitAudioRecording(tflite::ErrorReporter* error_reporter) {
|
||||
delay(10);
|
||||
|
||||
InitI2S();
|
||||
|
||||
xTaskCreatePinnedToCore(AudioRecordingTask, "AudioRecordingTask", 2048, NULL, 10, NULL, 0);
|
||||
|
||||
// Block until we have our first audio sample
|
||||
while (!g_latest_audio_timestamp) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter,
|
||||
int start_ms, int duration_ms,
|
||||
int* audio_samples_size, int16_t** audio_samples) {
|
||||
// Set everything up to start receiving audio
|
||||
if (!g_is_audio_initialized) {
|
||||
TfLiteStatus init_status = InitAudioRecording(error_reporter);
|
||||
if (init_status != kTfLiteOk) {
|
||||
return init_status;
|
||||
}
|
||||
g_is_audio_initialized = true;
|
||||
}
|
||||
// This next part should only be called when the main thread notices that the
|
||||
// latest audio sample data timestamp has changed, so that there's new data
|
||||
// in the capture ring buffer. The ring buffer will eventually wrap around and
|
||||
// overwrite the data, but the assumption is that the main thread is checking
|
||||
// often enough and the buffer is large enough that this call will be made
|
||||
// before that happens.
|
||||
|
||||
// Determine the index, in the history of all samples, of the first
|
||||
// sample we want
|
||||
const int start_offset = start_ms * (kAudioSampleFrequency / 1000);
|
||||
// Determine how many samples we want in total
|
||||
const int duration_sample_count =
|
||||
duration_ms * (kAudioSampleFrequency / 1000);
|
||||
for (int i = 0; i < duration_sample_count; ++i) {
|
||||
// For each sample, transform its index in the history of all samples into
|
||||
// its index in g_audio_capture_buffer
|
||||
const int capture_index = (start_offset + i) % kAudioCaptureBufferSize;
|
||||
// Write the sample to the output buffer
|
||||
g_audio_output_buffer[i] = g_audio_capture_buffer[capture_index];
|
||||
}
|
||||
|
||||
// Set pointers to provide access to the audio
|
||||
*audio_samples_size = kMaxAudioSampleSize;
|
||||
*audio_samples = g_audio_output_buffer;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
int32_t LatestAudioTimestamp() {
|
||||
return g_latest_audio_timestamp;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// This is an abstraction around an audio source like a microphone, and is
|
||||
// expected to return 16-bit PCM sample data for a given point in time. The
|
||||
// sample data itself should be used as quickly as possible by the caller, since
|
||||
// to allow memory optimizations there are no guarantees that the samples won't
|
||||
// be overwritten by new data in the future. In practice, implementations should
|
||||
// ensure that there's a reasonable time allowed for clients to access the data
|
||||
// before any reuse.
|
||||
// The reference implementation can have no platform-specific dependencies, so
|
||||
// it just returns an array filled with zeros. For real applications, you should
|
||||
// ensure there's a specialized implementation that accesses hardware APIs.
|
||||
TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter,
|
||||
int start_ms, int duration_ms,
|
||||
int* audio_samples_size, int16_t** audio_samples);
|
||||
|
||||
// Returns the time that audio data was last captured in milliseconds. There's
|
||||
// no contract about what time zero represents, the accuracy, or the granularity
|
||||
// of the result. Subsequent calls will generally not return a lower value, but
|
||||
// even that's not guaranteed if there's an overflow wraparound.
|
||||
// The reference implementation of this function just returns a constantly
|
||||
// incrementing value for each call, since it would need a non-portable platform
|
||||
// call to access time information. For real applications, you'll need to write
|
||||
// your own platform-specific implementation.
|
||||
int32_t LatestAudioTimestamp();
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_
|
||||
@@ -0,0 +1,89 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "command_responder.h"
|
||||
|
||||
#include <M5Atom.h>
|
||||
|
||||
int dispMode = 0;
|
||||
|
||||
void InitResponder() {
|
||||
M5.begin(true, false, true);
|
||||
}
|
||||
|
||||
namespace {
|
||||
enum {
|
||||
COMMAND_SILENCE,
|
||||
COMMAND_UNKNOWN,
|
||||
COMMAND_YES,
|
||||
COMMAND_NO,
|
||||
|
||||
COMMAND_MAX
|
||||
};
|
||||
uint8_t scoreList[COMMAND_MAX];
|
||||
uint8_t lastCommand;
|
||||
int8_t lastCommandTime;
|
||||
}
|
||||
|
||||
void RespondToCommand(tflite::ErrorReporter* error_reporter,
|
||||
int32_t current_time, const char* found_command,
|
||||
uint8_t score, bool is_new_command) {
|
||||
static int32_t last_timestamp = 0;
|
||||
|
||||
// Score List Update
|
||||
uint8_t command = COMMAND_SILENCE;
|
||||
memset(scoreList, 0, sizeof(scoreList));
|
||||
if (strcmp(found_command, "silence") == 0) {
|
||||
command = COMMAND_SILENCE;
|
||||
} else if (strcmp(found_command, "unknown") == 0) {
|
||||
command = COMMAND_UNKNOWN;
|
||||
} else if (strcmp(found_command, "yes") == 0) {
|
||||
command = COMMAND_YES;
|
||||
} else if (strcmp(found_command, "no") == 0) {
|
||||
command = COMMAND_NO;
|
||||
}
|
||||
scoreList[command] = score;
|
||||
|
||||
// New Command
|
||||
if (is_new_command) {
|
||||
lastCommand = command;
|
||||
lastCommandTime = 10;
|
||||
}
|
||||
|
||||
if (lastCommand == COMMAND_UNKNOWN && 0 < lastCommandTime) {
|
||||
M5.dis.drawpix(0, 0xf00000);
|
||||
} else if (lastCommand == COMMAND_YES && 0 < lastCommandTime) {
|
||||
M5.dis.drawpix(0, 0x0000f0);
|
||||
} else if (lastCommand == COMMAND_NO && 0 < lastCommandTime) {
|
||||
M5.dis.drawpix(0, 0x00f000);
|
||||
} else {
|
||||
M5.dis.drawpix(0, 0x707070);
|
||||
}
|
||||
|
||||
if (0 < lastCommandTime) {
|
||||
lastCommandTime--;
|
||||
}
|
||||
|
||||
Serial.printf("current_time(%d) found_command(%s) score(%d) is_new_command(%d)\n", current_time, found_command, score, is_new_command);
|
||||
}
|
||||
|
||||
void drawWave(int16_t value) {
|
||||
}
|
||||
|
||||
void drawInput(uint8_t *uint8) {
|
||||
}
|
||||
|
||||
void updateM5() {
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// Provides an interface to take an action based on an audio command.
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_COMMAND_RESPONDER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_COMMAND_RESPONDER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Called every time the results of an audio recognition run are available. The
|
||||
// human-readable name of any recognized command is in the `found_command`
|
||||
// argument, `score` has the numerical confidence, and `is_new_command` is set
|
||||
// if the previous command was different to this one.
|
||||
void RespondToCommand(tflite::ErrorReporter* error_reporter,
|
||||
int32_t current_time, const char* found_command,
|
||||
uint8_t score, bool is_new_command);
|
||||
|
||||
void InitResponder();
|
||||
void drawWave(int16_t value);
|
||||
void drawInput(uint8_t *uint8);
|
||||
void updateM5();
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_COMMAND_RESPONDER_H_
|
||||
@@ -0,0 +1,118 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "feature_provider.h"
|
||||
|
||||
#include "audio_provider.h"
|
||||
#include "micro_features_generator.h"
|
||||
#include "micro_model_settings.h"
|
||||
|
||||
FeatureProvider::FeatureProvider(int feature_size, uint8_t* feature_data)
|
||||
: feature_size_(feature_size),
|
||||
feature_data_(feature_data),
|
||||
is_first_run_(true) {
|
||||
// Initialize the feature data to default values.
|
||||
for (int n = 0; n < feature_size_; ++n) {
|
||||
feature_data_[n] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FeatureProvider::~FeatureProvider() {}
|
||||
|
||||
TfLiteStatus FeatureProvider::PopulateFeatureData(
|
||||
tflite::ErrorReporter* error_reporter, int32_t last_time_in_ms,
|
||||
int32_t time_in_ms, int* how_many_new_slices) {
|
||||
if (feature_size_ != kFeatureElementCount) {
|
||||
error_reporter->Report("Requested feature_data_ size %d doesn't match %d",
|
||||
feature_size_, kFeatureElementCount);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Quantize the time into steps as long as each window stride, so we can
|
||||
// figure out which audio data we need to fetch.
|
||||
const int last_step = (last_time_in_ms / kFeatureSliceStrideMs);
|
||||
const int current_step = (time_in_ms / kFeatureSliceStrideMs);
|
||||
|
||||
int slices_needed = current_step - last_step;
|
||||
// If this is the first call, make sure we don't use any cached information.
|
||||
if (is_first_run_) {
|
||||
TfLiteStatus init_status = InitializeMicroFeatures(error_reporter);
|
||||
if (init_status != kTfLiteOk) {
|
||||
return init_status;
|
||||
}
|
||||
is_first_run_ = false;
|
||||
slices_needed = kFeatureSliceCount;
|
||||
}
|
||||
if (slices_needed > kFeatureSliceCount) {
|
||||
slices_needed = kFeatureSliceCount;
|
||||
}
|
||||
*how_many_new_slices = slices_needed;
|
||||
|
||||
const int slices_to_keep = kFeatureSliceCount - slices_needed;
|
||||
const int slices_to_drop = kFeatureSliceCount - slices_to_keep;
|
||||
// If we can avoid recalculating some slices, just move the existing data
|
||||
// up in the spectrogram, to perform something like this:
|
||||
// last time = 80ms current time = 120ms
|
||||
// +-----------+ +-----------+
|
||||
// | data@20ms | --> | data@60ms |
|
||||
// +-----------+ -- +-----------+
|
||||
// | data@40ms | -- --> | data@80ms |
|
||||
// +-----------+ -- -- +-----------+
|
||||
// | data@60ms | -- -- | <empty> |
|
||||
// +-----------+ -- +-----------+
|
||||
// | data@80ms | -- | <empty> |
|
||||
// +-----------+ +-----------+
|
||||
if (slices_to_keep > 0) {
|
||||
for (int dest_slice = 0; dest_slice < slices_to_keep; ++dest_slice) {
|
||||
uint8_t* dest_slice_data =
|
||||
feature_data_ + (dest_slice * kFeatureSliceSize);
|
||||
const int src_slice = dest_slice + slices_to_drop;
|
||||
const uint8_t* src_slice_data =
|
||||
feature_data_ + (src_slice * kFeatureSliceSize);
|
||||
for (int i = 0; i < kFeatureSliceSize; ++i) {
|
||||
dest_slice_data[i] = src_slice_data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Any slices that need to be filled in with feature data have their
|
||||
// appropriate audio data pulled, and features calculated for that slice.
|
||||
if (slices_needed > 0) {
|
||||
for (int new_slice = slices_to_keep; new_slice < kFeatureSliceCount;
|
||||
++new_slice) {
|
||||
const int new_step = (current_step - kFeatureSliceCount + 1) + new_slice;
|
||||
const int32_t slice_start_ms = (new_step * kFeatureSliceStrideMs);
|
||||
int16_t* audio_samples = nullptr;
|
||||
int audio_samples_size = 0;
|
||||
// TODO(petewarden): Fix bug that leads to non-zero slice_start_ms
|
||||
GetAudioSamples(error_reporter, (slice_start_ms > 0 ? slice_start_ms : 0),
|
||||
kFeatureSliceDurationMs, &audio_samples_size,
|
||||
&audio_samples);
|
||||
if (audio_samples_size < kMaxAudioSampleSize) {
|
||||
error_reporter->Report("Audio data size %d too small, want %d",
|
||||
audio_samples_size, kMaxAudioSampleSize);
|
||||
return kTfLiteError;
|
||||
}
|
||||
uint8_t* new_slice_data = feature_data_ + (new_slice * kFeatureSliceSize);
|
||||
size_t num_samples_read;
|
||||
TfLiteStatus generate_status = GenerateMicroFeatures(
|
||||
error_reporter, audio_samples, audio_samples_size, kFeatureSliceSize,
|
||||
new_slice_data, &num_samples_read);
|
||||
if (generate_status != kTfLiteOk) {
|
||||
return generate_status;
|
||||
}
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Binds itself to an area of memory intended to hold the input features for an
|
||||
// audio-recognition neural network model, and fills that data area with the
|
||||
// features representing the current audio input, for example from a microphone.
|
||||
// The audio features themselves are a two-dimensional array, made up of
|
||||
// horizontal slices representing the frequencies at one point in time, stacked
|
||||
// on top of each other to form a spectrogram showing how those frequencies
|
||||
// changed over time.
|
||||
class FeatureProvider {
|
||||
public:
|
||||
// Create the provider, and bind it to an area of memory. This memory should
|
||||
// remain accessible for the lifetime of the provider object, since subsequent
|
||||
// calls will fill it with feature data. The provider does no memory
|
||||
// management of this data.
|
||||
FeatureProvider(int feature_size, uint8_t* feature_data);
|
||||
~FeatureProvider();
|
||||
|
||||
// Fills the feature data with information from audio inputs, and returns how
|
||||
// many feature slices were updated.
|
||||
TfLiteStatus PopulateFeatureData(tflite::ErrorReporter* error_reporter,
|
||||
int32_t last_time_in_ms, int32_t time_in_ms,
|
||||
int* how_many_new_slices);
|
||||
|
||||
private:
|
||||
int feature_size_;
|
||||
uint8_t* feature_data_;
|
||||
// Make sure we don't try to use cached information if this is the first call
|
||||
// into the provider.
|
||||
bool is_first_run_;
|
||||
};
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MAIN_FUNCTIONS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MAIN_FUNCTIONS_H_
|
||||
|
||||
// Initializes all data needed for the example. The name is important, and needs
|
||||
// to be setup() for Arduino compatibility.
|
||||
void setup();
|
||||
|
||||
// Runs one iteration of data gathering and inference. This should be called
|
||||
// repeatedly from the application code. The name needs to be loop() for Arduino
|
||||
// compatibility.
|
||||
void loop();
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MAIN_FUNCTIONS_H_
|
||||
@@ -0,0 +1,102 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "micro_features_generator.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "micro_model_settings.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/frontend_util.h"
|
||||
|
||||
// Configure FFT to output 16 bit fixed point.
|
||||
#define FIXED_POINT 16
|
||||
|
||||
namespace {
|
||||
|
||||
FrontendState g_micro_features_state;
|
||||
bool g_is_first_time = true;
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteStatus InitializeMicroFeatures(tflite::ErrorReporter* error_reporter) {
|
||||
FrontendConfig config;
|
||||
config.window.size_ms = kFeatureSliceDurationMs;
|
||||
config.window.step_size_ms = kFeatureSliceStrideMs;
|
||||
config.noise_reduction.smoothing_bits = 10;
|
||||
config.filterbank.num_channels = kFeatureSliceSize;
|
||||
config.filterbank.lower_band_limit = 125.0;
|
||||
config.filterbank.upper_band_limit = 7500.0;
|
||||
config.noise_reduction.smoothing_bits = 10;
|
||||
config.noise_reduction.even_smoothing = 0.025;
|
||||
config.noise_reduction.odd_smoothing = 0.06;
|
||||
config.noise_reduction.min_signal_remaining = 0.05;
|
||||
config.pcan_gain_control.enable_pcan = 1;
|
||||
config.pcan_gain_control.strength = 0.95;
|
||||
config.pcan_gain_control.offset = 80.0;
|
||||
config.pcan_gain_control.gain_bits = 21;
|
||||
config.log_scale.enable_log = 1;
|
||||
config.log_scale.scale_shift = 6;
|
||||
if (!FrontendPopulateState(&config, &g_micro_features_state,
|
||||
kAudioSampleFrequency)) {
|
||||
error_reporter->Report("FrontendPopulateState() failed");
|
||||
return kTfLiteError;
|
||||
}
|
||||
g_is_first_time = true;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// This is not exposed in any header, and is only used for testing, to ensure
|
||||
// that the state is correctly set up before generating results.
|
||||
void SetMicroFeaturesNoiseEstimates(const uint32_t* estimate_presets) {
|
||||
for (int i = 0; i < g_micro_features_state.filterbank.num_channels; ++i) {
|
||||
g_micro_features_state.noise_reduction.estimate[i] = estimate_presets[i];
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus GenerateMicroFeatures(tflite::ErrorReporter* error_reporter,
|
||||
const int16_t* input, int input_size,
|
||||
int output_size, uint8_t* output,
|
||||
size_t* num_samples_read) {
|
||||
const int16_t* frontend_input;
|
||||
if (g_is_first_time) {
|
||||
frontend_input = input;
|
||||
g_is_first_time = false;
|
||||
} else {
|
||||
frontend_input = input + 160;
|
||||
}
|
||||
FrontendOutput frontend_output = FrontendProcessSamples(
|
||||
&g_micro_features_state, frontend_input, input_size, num_samples_read);
|
||||
|
||||
for (int i = 0; i < frontend_output.size; ++i) {
|
||||
// These scaling values are derived from those used in input_data.py in the
|
||||
// training pipeline.
|
||||
constexpr int32_t value_scale = (10 * 255);
|
||||
constexpr int32_t value_div = (256 * 26);
|
||||
int32_t value =
|
||||
((frontend_output.values[i] * value_scale) + (value_div / 2)) /
|
||||
value_div;
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
}
|
||||
if (value > 255) {
|
||||
value = 255;
|
||||
}
|
||||
output[i] = value;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_FEATURES_GENERATOR_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_FEATURES_GENERATOR_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Sets up any resources needed for the feature generation pipeline.
|
||||
TfLiteStatus InitializeMicroFeatures(tflite::ErrorReporter* error_reporter);
|
||||
|
||||
// Converts audio sample data into a more compact form that's appropriate for
|
||||
// feeding into a neural network.
|
||||
TfLiteStatus GenerateMicroFeatures(tflite::ErrorReporter* error_reporter,
|
||||
const int16_t* input, int input_size,
|
||||
int output_size, uint8_t* output,
|
||||
size_t* num_samples_read);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_FEATURES_GENERATOR_H_
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "micro_model_settings.h"
|
||||
|
||||
const char* kCategoryLabels[kCategoryCount] = {
|
||||
"silence",
|
||||
"unknown",
|
||||
"yes",
|
||||
"no",
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_MODEL_SETTINGS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_MODEL_SETTINGS_H_
|
||||
|
||||
// Keeping these as constant expressions allow us to allocate fixed-sized arrays
|
||||
// on the stack for our working memory.
|
||||
|
||||
// The size of the input time series data we pass to the FFT to produce the
|
||||
// frequency information. This has to be a power of two, and since we're dealing
|
||||
// with 30ms of 16KHz inputs, which means 480 samples, this is the next value.
|
||||
constexpr int kMaxAudioSampleSize = 512;
|
||||
constexpr int kAudioSampleFrequency = 16000;
|
||||
|
||||
// All of these values are derived from the values used during model training,
|
||||
// if you change your model you'll need to update these constants.
|
||||
constexpr int kFeatureSliceSize = 40;
|
||||
constexpr int kFeatureSliceCount = 49;
|
||||
constexpr int kFeatureElementCount = (kFeatureSliceSize * kFeatureSliceCount);
|
||||
constexpr int kFeatureSliceStrideMs = 20;
|
||||
constexpr int kFeatureSliceDurationMs = 30;
|
||||
|
||||
constexpr int kCategoryCount = 4;
|
||||
constexpr int kSilenceIndex = 0;
|
||||
constexpr int kUnknownIndex = 1;
|
||||
extern const char* kCategoryLabels[kCategoryCount];
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_MODEL_SETTINGS_H_
|
||||
@@ -0,0 +1,197 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include <TensorFlowLite_ESP32.h>
|
||||
|
||||
#include "main_functions.h"
|
||||
|
||||
#include "audio_provider.h"
|
||||
#include "command_responder.h"
|
||||
#include "feature_provider.h"
|
||||
#include "micro_model_settings.h"
|
||||
#include "tiny_conv_micro_features_model_data.h"
|
||||
#include "recognize_commands.h"
|
||||
#include "tensorflow/lite/experimental/micro/kernels/micro_ops.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_interpreter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_mutable_op_resolver.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
#include "tensorflow/lite/version.h"
|
||||
|
||||
// Globals, used for compatibility with Arduino-style sketches.
|
||||
namespace {
|
||||
tflite::ErrorReporter* error_reporter = nullptr;
|
||||
const tflite::Model* model = nullptr;
|
||||
tflite::MicroInterpreter* interpreter = nullptr;
|
||||
TfLiteTensor* model_input = nullptr;
|
||||
FeatureProvider* feature_provider = nullptr;
|
||||
RecognizeCommands* recognizer = nullptr;
|
||||
int32_t previous_time = 0;
|
||||
|
||||
// Create an area of memory to use for input, output, and intermediate arrays.
|
||||
// The size of this will depend on the model you're using, and may need to be
|
||||
// determined by experimentation.
|
||||
constexpr int kTensorArenaSize = 10 * 1024;
|
||||
uint8_t tensor_arena[kTensorArenaSize];
|
||||
} // namespace
|
||||
|
||||
QueueHandle_t xQueueAudioWave;
|
||||
#define QueueAudioWaveSize 32
|
||||
|
||||
// The name of this function is important for Arduino compatibility.
|
||||
void setup() {
|
||||
xQueueAudioWave = xQueueCreate(QueueAudioWaveSize, sizeof(int16_t));
|
||||
|
||||
// Set up logging. Google style is to avoid globals or statics because of
|
||||
// lifetime uncertainty, but since this has a trivial destructor it's okay.
|
||||
// NOLINTNEXTLINE(runtime-global-variables)
|
||||
static tflite::MicroErrorReporter micro_error_reporter;
|
||||
error_reporter = µ_error_reporter;
|
||||
|
||||
// Map the model into a usable data structure. This doesn't involve any
|
||||
// copying or parsing, it's a very lightweight operation.
|
||||
model = tflite::GetModel(g_tiny_conv_micro_features_model_data);
|
||||
if (model->version() != TFLITE_SCHEMA_VERSION) {
|
||||
error_reporter->Report(
|
||||
"Model provided is schema version %d not equal "
|
||||
"to supported version %d.",
|
||||
model->version(), TFLITE_SCHEMA_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pull in only the operation implementations we need.
|
||||
// This relies on a complete list of all the ops needed by this graph.
|
||||
// An easier approach is to just use the AllOpsResolver, but this will
|
||||
// incur some penalty in code space for op implementations that are not
|
||||
// needed by this graph.
|
||||
//
|
||||
// tflite::ops::micro::AllOpsResolver resolver;
|
||||
// NOLINTNEXTLINE(runtime-global-variables)
|
||||
static tflite::MicroMutableOpResolver micro_mutable_op_resolver;
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_DEPTHWISE_CONV_2D,
|
||||
tflite::ops::micro::Register_DEPTHWISE_CONV_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_FULLY_CONNECTED,
|
||||
tflite::ops::micro::Register_FULLY_CONNECTED());
|
||||
micro_mutable_op_resolver.AddBuiltin(tflite::BuiltinOperator_SOFTMAX,
|
||||
tflite::ops::micro::Register_SOFTMAX());
|
||||
|
||||
// Build an interpreter to run the model with.
|
||||
static tflite::MicroInterpreter static_interpreter(
|
||||
model, micro_mutable_op_resolver, tensor_arena, kTensorArenaSize,
|
||||
error_reporter);
|
||||
interpreter = &static_interpreter;
|
||||
|
||||
// Allocate memory from the tensor_arena for the model's tensors.
|
||||
TfLiteStatus allocate_status = interpreter->AllocateTensors();
|
||||
if (allocate_status != kTfLiteOk) {
|
||||
error_reporter->Report("AllocateTensors() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get information about the memory area to use for the model's input.
|
||||
model_input = interpreter->input(0);
|
||||
if ((model_input->dims->size != 4) || (model_input->dims->data[0] != 1) ||
|
||||
(model_input->dims->data[1] != kFeatureSliceCount) ||
|
||||
(model_input->dims->data[2] != kFeatureSliceSize) ||
|
||||
(model_input->type != kTfLiteUInt8)) {
|
||||
error_reporter->Report("Bad input tensor parameters in model");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare to access the audio spectrograms from a microphone or other source
|
||||
// that will provide the inputs to the neural network.
|
||||
// NOLINTNEXTLINE(runtime-global-variables)
|
||||
static FeatureProvider static_feature_provider(kFeatureElementCount,
|
||||
model_input->data.uint8);
|
||||
feature_provider = &static_feature_provider;
|
||||
|
||||
static RecognizeCommands static_recognizer(error_reporter);
|
||||
recognizer = &static_recognizer;
|
||||
|
||||
previous_time = 0;
|
||||
|
||||
InitResponder();
|
||||
|
||||
Serial.printf("model_input->name : %s\n", model_input->name);
|
||||
Serial.printf("model_input->type : %d\n", model_input->type);
|
||||
Serial.printf("model_input->bytes : %d\n", model_input->bytes);
|
||||
Serial.printf("model_input->dims->size : %d\n", model_input->dims->size);
|
||||
Serial.printf("model_input->dims->data[0] : %d\n", model_input->dims->data[0]); // 1
|
||||
Serial.printf("model_input->dims->data[1] : %d\n", model_input->dims->data[1]); // kFeatureSliceCount
|
||||
Serial.printf("model_input->dims->data[2] : %d\n", model_input->dims->data[2]); // kFeatureSliceSize
|
||||
}
|
||||
|
||||
// The name of this function is important for Arduino compatibility.
|
||||
void loop() {
|
||||
updateM5();
|
||||
|
||||
int16_t wave = 0;
|
||||
for (int i = 0; i < QueueAudioWaveSize; i++) {
|
||||
if (xQueueReceive(xQueueAudioWave, &wave, 0) == pdTRUE) {
|
||||
drawWave(wave);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the spectrogram for the current time.
|
||||
const int32_t current_time = LatestAudioTimestamp();
|
||||
int how_many_new_slices = 0;
|
||||
TfLiteStatus feature_status = feature_provider->PopulateFeatureData(
|
||||
error_reporter, previous_time, current_time, &how_many_new_slices);
|
||||
if (feature_status != kTfLiteOk) {
|
||||
error_reporter->Report("Feature generation failed");
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
previous_time = current_time;
|
||||
// If no new audio samples have been received since last time, don't bother
|
||||
// running the network model.
|
||||
if (how_many_new_slices == 0) {
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Run the model on the spectrogram input and make sure it succeeds.
|
||||
TfLiteStatus invoke_status = interpreter->Invoke();
|
||||
if (invoke_status != kTfLiteOk) {
|
||||
error_reporter->Report("Invoke failed");
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Obtain a pointer to the output tensor
|
||||
TfLiteTensor* output = interpreter->output(0);
|
||||
// Determine whether a command was recognized based on the output of inference
|
||||
const char* found_command = nullptr;
|
||||
uint8_t score = 0;
|
||||
bool is_new_command = false;
|
||||
TfLiteStatus process_status = recognizer->ProcessLatestResults(
|
||||
output, current_time, &found_command, &score, &is_new_command);
|
||||
if (process_status != kTfLiteOk) {
|
||||
error_reporter->Report("RecognizeCommands::ProcessLatestResults() failed");
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
// Do something based on the recognized command. The default implementation
|
||||
// just prints to the error console, but you should replace this with your
|
||||
// own function for a real application.
|
||||
RespondToCommand(error_reporter, current_time, found_command, score,
|
||||
is_new_command);
|
||||
|
||||
drawInput(model_input->data.uint8);
|
||||
|
||||
delay(1);
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "no_micro_features_data.h"
|
||||
|
||||
/* File automatically created by
|
||||
* tensorflow/examples/speech_commands/wav_to_features.py \
|
||||
* --sample_rate=16000 \
|
||||
* --clip_duration_ms=1000 \
|
||||
* --window_size_ms=30 \
|
||||
* --window_stride_ms=20 \
|
||||
* --feature_bin_count=40 \
|
||||
* --quantize=1 \
|
||||
* --preprocess="micro" \
|
||||
* --input_wav="speech_commands_test_set_v0.02/no/f9643d42_nohash_4.wav" \
|
||||
* --output_c_file="/tmp/no_micro_features_data.cc" \
|
||||
*/
|
||||
|
||||
const int g_no_micro_f9643d42_nohash_4_width = 40;
|
||||
const int g_no_micro_f9643d42_nohash_4_height = 49;
|
||||
const unsigned char g_no_micro_f9643d42_nohash_4_data[] = {
|
||||
230, 205, 191, 203, 202, 181, 180, 194, 205, 187, 183, 197, 203, 198, 196,
|
||||
186, 202, 159, 151, 126, 110, 138, 141, 142, 137, 148, 133, 120, 110, 126,
|
||||
117, 110, 117, 116, 137, 134, 95, 116, 123, 110, 184, 144, 183, 189, 197,
|
||||
172, 188, 164, 194, 179, 175, 174, 182, 173, 184, 174, 200, 145, 154, 148,
|
||||
147, 135, 143, 122, 127, 138, 116, 99, 122, 105, 110, 125, 127, 133, 131,
|
||||
123, 116, 119, 127, 114, 193, 176, 185, 170, 175, 146, 166, 167, 185, 185,
|
||||
185, 183, 195, 185, 176, 178, 197, 155, 137, 144, 164, 132, 153, 132, 138,
|
||||
137, 134, 95, 120, 116, 131, 122, 99, 120, 120, 110, 116, 110, 126, 127,
|
||||
128, 159, 187, 119, 178, 187, 197, 167, 199, 184, 180, 165, 194, 176, 144,
|
||||
134, 187, 136, 142, 134, 145, 132, 145, 105, 119, 123, 125, 116, 125, 102,
|
||||
129, 138, 130, 99, 99, 90, 120, 123, 134, 95, 194, 172, 187, 123, 191,
|
||||
179, 195, 182, 201, 137, 167, 142, 185, 161, 187, 146, 167, 152, 154, 107,
|
||||
152, 112, 134, 144, 117, 116, 105, 85, 105, 105, 99, 90, 123, 112, 112,
|
||||
68, 107, 105, 117, 99, 116, 143, 139, 90, 154, 142, 188, 172, 178, 135,
|
||||
175, 149, 177, 110, 173, 160, 169, 162, 173, 119, 132, 110, 85, 85, 117,
|
||||
129, 117, 112, 117, 51, 112, 95, 139, 102, 105, 90, 128, 119, 112, 99,
|
||||
170, 168, 195, 152, 174, 173, 180, 0, 157, 130, 169, 149, 149, 123, 170,
|
||||
130, 170, 133, 159, 102, 134, 90, 85, 105, 126, 119, 130, 90, 78, 68,
|
||||
127, 120, 95, 51, 122, 110, 112, 78, 116, 95, 180, 135, 179, 146, 179,
|
||||
162, 197, 153, 172, 135, 154, 0, 149, 95, 145, 114, 166, 0, 114, 110,
|
||||
145, 107, 114, 90, 136, 68, 95, 95, 95, 85, 116, 99, 116, 0, 95,
|
||||
68, 102, 51, 102, 78, 185, 157, 138, 158, 180, 117, 173, 142, 145, 117,
|
||||
169, 130, 159, 99, 138, 123, 169, 90, 78, 0, 123, 85, 107, 51, 114,
|
||||
102, 95, 0, 116, 85, 119, 95, 95, 68, 85, 51, 116, 68, 102, 78,
|
||||
167, 105, 164, 163, 178, 126, 164, 154, 154, 51, 177, 120, 156, 85, 134,
|
||||
139, 168, 90, 161, 102, 114, 116, 122, 95, 112, 102, 107, 51, 114, 85,
|
||||
119, 78, 114, 90, 102, 51, 102, 51, 114, 99, 177, 68, 152, 102, 184,
|
||||
166, 179, 129, 177, 129, 180, 110, 158, 105, 139, 0, 145, 85, 148, 102,
|
||||
117, 102, 116, 0, 78, 68, 90, 51, 107, 85, 78, 0, 51, 0, 51,
|
||||
0, 95, 51, 107, 68, 180, 117, 90, 0, 138, 0, 187, 146, 119, 140,
|
||||
164, 90, 136, 0, 131, 51, 159, 99, 141, 138, 116, 51, 90, 51, 90,
|
||||
68, 105, 0, 85, 78, 112, 51, 122, 95, 128, 68, 85, 0, 112, 68,
|
||||
147, 126, 178, 146, 171, 130, 190, 147, 188, 123, 170, 78, 132, 0, 130,
|
||||
125, 159, 95, 102, 0, 110, 0, 95, 85, 120, 68, 78, 51, 99, 51,
|
||||
105, 0, 112, 102, 105, 68, 90, 51, 90, 0, 127, 95, 166, 175, 187,
|
||||
133, 135, 0, 171, 139, 132, 128, 140, 51, 126, 107, 161, 0, 95, 51,
|
||||
119, 0, 114, 0, 95, 110, 116, 51, 112, 0, 90, 0, 116, 51, 68,
|
||||
0, 105, 68, 105, 0, 164, 78, 173, 0, 194, 166, 145, 114, 116, 51,
|
||||
107, 122, 151, 0, 156, 102, 148, 51, 122, 95, 129, 0, 85, 0, 127,
|
||||
78, 90, 0, 78, 0, 95, 0, 110, 0, 68, 119, 120, 68, 68, 0,
|
||||
122, 99, 147, 127, 200, 167, 85, 114, 161, 85, 161, 125, 143, 99, 156,
|
||||
85, 147, 68, 99, 0, 107, 102, 132, 51, 112, 68, 95, 78, 99, 0,
|
||||
68, 0, 51, 0, 90, 78, 128, 51, 95, 0, 166, 136, 174, 138, 189,
|
||||
144, 130, 129, 138, 134, 132, 120, 134, 0, 51, 78, 147, 51, 51, 0,
|
||||
51, 0, 78, 0, 68, 68, 95, 78, 90, 0, 0, 0, 68, 0, 90,
|
||||
68, 110, 0, 95, 51, 165, 151, 157, 0, 0, 0, 112, 0, 112, 95,
|
||||
149, 107, 119, 68, 126, 68, 138, 0, 78, 0, 78, 0, 99, 51, 112,
|
||||
0, 102, 0, 78, 51, 85, 0, 0, 0, 78, 0, 95, 0, 95, 78,
|
||||
105, 0, 152, 0, 0, 51, 132, 105, 159, 0, 129, 102, 114, 0, 138,
|
||||
51, 123, 0, 129, 78, 119, 51, 51, 51, 105, 0, 78, 85, 95, 0,
|
||||
85, 0, 0, 0, 85, 0, 78, 0, 0, 0, 172, 142, 141, 0, 137,
|
||||
0, 148, 128, 157, 120, 146, 120, 120, 0, 95, 78, 141, 68, 68, 0,
|
||||
68, 0, 90, 0, 85, 0, 107, 0, 78, 0, 85, 51, 102, 0, 68,
|
||||
78, 68, 0, 51, 0, 125, 0, 141, 51, 102, 138, 175, 51, 120, 51,
|
||||
173, 85, 116, 141, 164, 68, 150, 123, 133, 51, 114, 0, 117, 68, 150,
|
||||
51, 116, 68, 78, 0, 68, 0, 68, 0, 85, 0, 78, 0, 51, 78,
|
||||
155, 90, 161, 0, 132, 99, 123, 78, 107, 0, 134, 90, 95, 0, 78,
|
||||
0, 162, 143, 85, 0, 107, 78, 125, 90, 90, 51, 51, 0, 85, 0,
|
||||
0, 0, 132, 102, 102, 154, 128, 0, 99, 68, 162, 102, 151, 0, 99,
|
||||
51, 147, 141, 156, 0, 112, 120, 158, 127, 145, 139, 187, 171, 135, 138,
|
||||
146, 0, 95, 68, 127, 0, 85, 0, 105, 0, 0, 0, 187, 170, 162,
|
||||
188, 165, 51, 51, 78, 243, 215, 225, 196, 205, 181, 205, 168, 176, 134,
|
||||
157, 110, 126, 114, 133, 139, 193, 163, 159, 116, 160, 126, 122, 127, 171,
|
||||
99, 114, 68, 123, 85, 90, 0, 157, 146, 166, 179, 136, 0, 116, 90,
|
||||
242, 219, 240, 204, 216, 164, 188, 171, 176, 164, 154, 158, 190, 157, 190,
|
||||
141, 182, 177, 169, 128, 172, 145, 105, 129, 157, 90, 78, 51, 119, 68,
|
||||
137, 68, 116, 78, 141, 132, 151, 122, 156, 140, 234, 206, 229, 201, 216,
|
||||
174, 191, 144, 162, 85, 122, 157, 194, 167, 204, 149, 180, 166, 166, 139,
|
||||
122, 133, 156, 126, 145, 85, 128, 0, 99, 51, 145, 0, 126, 51, 166,
|
||||
162, 166, 162, 177, 157, 228, 198, 221, 197, 214, 177, 173, 166, 173, 139,
|
||||
185, 191, 202, 163, 205, 172, 206, 189, 135, 68, 166, 134, 149, 134, 135,
|
||||
90, 127, 107, 175, 90, 136, 117, 135, 140, 172, 167, 166, 149, 177, 152,
|
||||
221, 191, 215, 194, 211, 0, 156, 147, 182, 178, 208, 163, 190, 157, 208,
|
||||
200, 195, 164, 179, 154, 181, 150, 143, 99, 132, 137, 185, 143, 163, 85,
|
||||
51, 107, 132, 134, 164, 127, 167, 159, 175, 141, 216, 195, 223, 211, 238,
|
||||
223, 243, 215, 226, 204, 232, 211, 232, 213, 240, 218, 235, 214, 238, 205,
|
||||
207, 173, 149, 201, 215, 200, 230, 213, 208, 195, 175, 151, 195, 175, 182,
|
||||
163, 235, 217, 218, 190, 211, 191, 215, 191, 217, 220, 241, 215, 229, 206,
|
||||
236, 210, 227, 216, 236, 188, 183, 149, 202, 189, 208, 172, 191, 201, 220,
|
||||
193, 221, 207, 216, 208, 201, 131, 170, 187, 229, 197, 211, 194, 226, 201,
|
||||
205, 184, 206, 177, 221, 210, 226, 184, 204, 197, 218, 198, 212, 209, 213,
|
||||
141, 172, 110, 175, 167, 180, 156, 213, 188, 192, 179, 213, 205, 204, 174,
|
||||
200, 147, 162, 181, 203, 167, 198, 187, 210, 164, 196, 169, 189, 168, 224,
|
||||
198, 213, 204, 198, 195, 230, 211, 221, 197, 208, 0, 0, 0, 85, 90,
|
||||
167, 130, 175, 173, 203, 164, 193, 144, 170, 145, 185, 148, 154, 139, 198,
|
||||
159, 180, 171, 216, 174, 178, 161, 166, 136, 216, 184, 215, 197, 199, 190,
|
||||
228, 195, 208, 51, 117, 0, 0, 0, 0, 0, 140, 51, 135, 154, 188,
|
||||
155, 168, 0, 90, 0, 156, 85, 110, 0, 174, 90, 172, 154, 179, 99,
|
||||
142, 166, 179, 157, 177, 95, 192, 142, 204, 198, 217, 147, 173, 0, 112,
|
||||
0, 0, 0, 0, 0, 0, 0, 110, 0, 107, 0, 160, 0, 148, 95,
|
||||
172, 0, 0, 0, 116, 0, 122, 114, 170, 0, 0, 0, 0, 0, 179,
|
||||
110, 196, 85, 205, 183, 169, 0, 99, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 141, 0, 112, 0, 0, 0, 134, 0, 0, 0, 0,
|
||||
0, 0, 0, 139, 0, 0, 0, 0, 112, 186, 78, 163, 0, 169, 128,
|
||||
174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95,
|
||||
0, 105, 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, 0, 95, 0,
|
||||
0, 0, 0, 0, 0, 0, 119, 0, 164, 78, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 68,
|
||||
117, 0, 0, 0, 0, 0, 0, 0, 148, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
|
||||
0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_NO_MICRO_FEATURES_DATA_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_NO_MICRO_FEATURES_DATA_H_
|
||||
|
||||
extern const int g_no_micro_f9643d42_nohash_4_width;
|
||||
extern const int g_no_micro_f9643d42_nohash_4_height;
|
||||
extern const unsigned char g_no_micro_f9643d42_nohash_4_data[];
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_NO_MICRO_FEATURES_DATA_H_
|
||||
@@ -0,0 +1,139 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "recognize_commands.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
RecognizeCommands::RecognizeCommands(tflite::ErrorReporter* error_reporter,
|
||||
int32_t average_window_duration_ms,
|
||||
uint8_t detection_threshold,
|
||||
int32_t suppression_ms,
|
||||
int32_t minimum_count)
|
||||
: error_reporter_(error_reporter),
|
||||
average_window_duration_ms_(average_window_duration_ms),
|
||||
detection_threshold_(detection_threshold),
|
||||
suppression_ms_(suppression_ms),
|
||||
minimum_count_(minimum_count),
|
||||
previous_results_(error_reporter) {
|
||||
previous_top_label_ = "silence";
|
||||
previous_top_label_time_ = std::numeric_limits<int32_t>::min();
|
||||
}
|
||||
|
||||
TfLiteStatus RecognizeCommands::ProcessLatestResults(
|
||||
const TfLiteTensor* latest_results, const int32_t current_time_ms,
|
||||
const char** found_command, uint8_t* score, bool* is_new_command) {
|
||||
if ((latest_results->dims->size != 2) ||
|
||||
(latest_results->dims->data[0] != 1) ||
|
||||
(latest_results->dims->data[1] != kCategoryCount)) {
|
||||
error_reporter_->Report(
|
||||
"The results for recognition should contain %d elements, but there are "
|
||||
"%d in an %d-dimensional shape",
|
||||
kCategoryCount, latest_results->dims->data[1],
|
||||
latest_results->dims->size);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if (latest_results->type != kTfLiteUInt8) {
|
||||
error_reporter_->Report(
|
||||
"The results for recognition should be uint8 elements, but are %d",
|
||||
latest_results->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if ((!previous_results_.empty()) &&
|
||||
(current_time_ms < previous_results_.front().time_)) {
|
||||
error_reporter_->Report(
|
||||
"Results must be fed in increasing time order, but received a "
|
||||
"timestamp of %d that was earlier than the previous one of %d",
|
||||
current_time_ms, previous_results_.front().time_);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Add the latest results to the head of the queue.
|
||||
previous_results_.push_back({current_time_ms, latest_results->data.uint8});
|
||||
|
||||
// Prune any earlier results that are too old for the averaging window.
|
||||
const int64_t time_limit = current_time_ms - average_window_duration_ms_;
|
||||
while ((!previous_results_.empty()) &&
|
||||
previous_results_.front().time_ < time_limit) {
|
||||
previous_results_.pop_front();
|
||||
}
|
||||
|
||||
// If there are too few results, assume the result will be unreliable and
|
||||
// bail.
|
||||
const int64_t how_many_results = previous_results_.size();
|
||||
const int64_t earliest_time = previous_results_.front().time_;
|
||||
const int64_t samples_duration = current_time_ms - earliest_time;
|
||||
if ((how_many_results < minimum_count_) ||
|
||||
(samples_duration < (average_window_duration_ms_ / 4))) {
|
||||
*found_command = previous_top_label_;
|
||||
*score = 0;
|
||||
*is_new_command = false;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Calculate the average score across all the results in the window.
|
||||
int32_t average_scores[kCategoryCount];
|
||||
for (int offset = 0; offset < previous_results_.size(); ++offset) {
|
||||
PreviousResultsQueue::Result previous_result =
|
||||
previous_results_.from_front(offset);
|
||||
const uint8_t* scores = previous_result.scores_;
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
if (offset == 0) {
|
||||
average_scores[i] = scores[i];
|
||||
} else {
|
||||
average_scores[i] += scores[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
average_scores[i] /= how_many_results;
|
||||
}
|
||||
|
||||
// Find the current highest scoring category.
|
||||
int current_top_index = 0;
|
||||
int32_t current_top_score = 0;
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
if (average_scores[i] > current_top_score) {
|
||||
current_top_score = average_scores[i];
|
||||
current_top_index = i;
|
||||
}
|
||||
}
|
||||
const char* current_top_label = kCategoryLabels[current_top_index];
|
||||
|
||||
// If we've recently had another label trigger, assume one that occurs too
|
||||
// soon afterwards is a bad result.
|
||||
int64_t time_since_last_top;
|
||||
if ((previous_top_label_ == kCategoryLabels[0]) ||
|
||||
(previous_top_label_time_ == std::numeric_limits<int32_t>::min())) {
|
||||
time_since_last_top = std::numeric_limits<int32_t>::max();
|
||||
} else {
|
||||
time_since_last_top = current_time_ms - previous_top_label_time_;
|
||||
}
|
||||
if ((current_top_score > detection_threshold_) &&
|
||||
((current_top_label != previous_top_label_) ||
|
||||
(time_since_last_top > suppression_ms_))) {
|
||||
previous_top_label_ = current_top_label;
|
||||
previous_top_label_time_ = current_time_ms;
|
||||
*is_new_command = true;
|
||||
} else {
|
||||
*is_new_command = false;
|
||||
}
|
||||
*found_command = current_top_label;
|
||||
*score = current_top_score;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "micro_model_settings.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Partial implementation of std::dequeue, just providing the functionality
|
||||
// that's needed to keep a record of previous neural network results over a
|
||||
// short time period, so they can be averaged together to produce a more
|
||||
// accurate overall prediction. This doesn't use any dynamic memory allocation
|
||||
// so it's a better fit for microcontroller applications, but this does mean
|
||||
// there are hard limits on the number of results it can store.
|
||||
class PreviousResultsQueue {
|
||||
public:
|
||||
PreviousResultsQueue(tflite::ErrorReporter* error_reporter)
|
||||
: error_reporter_(error_reporter), front_index_(0), size_(0) {}
|
||||
|
||||
// Data structure that holds an inference result, and the time when it
|
||||
// was recorded.
|
||||
struct Result {
|
||||
Result() : time_(0), scores_() {}
|
||||
Result(int32_t time, uint8_t* scores) : time_(time) {
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
scores_[i] = scores[i];
|
||||
}
|
||||
}
|
||||
int32_t time_;
|
||||
uint8_t scores_[kCategoryCount];
|
||||
};
|
||||
|
||||
int size() { return size_; }
|
||||
bool empty() { return size_ == 0; }
|
||||
Result& front() { return results_[front_index_]; }
|
||||
Result& back() {
|
||||
int back_index = front_index_ + (size_ - 1);
|
||||
if (back_index >= kMaxResults) {
|
||||
back_index -= kMaxResults;
|
||||
}
|
||||
return results_[back_index];
|
||||
}
|
||||
|
||||
void push_back(const Result& entry) {
|
||||
if (size() >= kMaxResults) {
|
||||
error_reporter_->Report(
|
||||
"Couldn't push_back latest result, too many already!");
|
||||
return;
|
||||
}
|
||||
size_ += 1;
|
||||
back() = entry;
|
||||
}
|
||||
|
||||
Result pop_front() {
|
||||
if (size() <= 0) {
|
||||
error_reporter_->Report("Couldn't pop_front result, none present!");
|
||||
return Result();
|
||||
}
|
||||
Result result = front();
|
||||
front_index_ += 1;
|
||||
if (front_index_ >= kMaxResults) {
|
||||
front_index_ = 0;
|
||||
}
|
||||
size_ -= 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Most of the functions are duplicates of dequeue containers, but this
|
||||
// is a helper that makes it easy to iterate through the contents of the
|
||||
// queue.
|
||||
Result& from_front(int offset) {
|
||||
if ((offset < 0) || (offset >= size_)) {
|
||||
error_reporter_->Report("Attempt to read beyond the end of the queue!");
|
||||
offset = size_ - 1;
|
||||
}
|
||||
int index = front_index_ + offset;
|
||||
if (index >= kMaxResults) {
|
||||
index -= kMaxResults;
|
||||
}
|
||||
return results_[index];
|
||||
}
|
||||
|
||||
private:
|
||||
tflite::ErrorReporter* error_reporter_;
|
||||
static constexpr int kMaxResults = 50;
|
||||
Result results_[kMaxResults];
|
||||
|
||||
int front_index_;
|
||||
int size_;
|
||||
};
|
||||
|
||||
// This class is designed to apply a very primitive decoding model on top of the
|
||||
// instantaneous results from running an audio recognition model on a single
|
||||
// window of samples. It applies smoothing over time so that noisy individual
|
||||
// label scores are averaged, increasing the confidence that apparent matches
|
||||
// are real.
|
||||
// To use it, you should create a class object with the configuration you
|
||||
// want, and then feed results from running a TensorFlow model into the
|
||||
// processing method. The timestamp for each subsequent call should be
|
||||
// increasing from the previous, since the class is designed to process a stream
|
||||
// of data over time.
|
||||
class RecognizeCommands {
|
||||
public:
|
||||
// labels should be a list of the strings associated with each one-hot score.
|
||||
// The window duration controls the smoothing. Longer durations will give a
|
||||
// higher confidence that the results are correct, but may miss some commands.
|
||||
// The detection threshold has a similar effect, with high values increasing
|
||||
// the precision at the cost of recall. The minimum count controls how many
|
||||
// results need to be in the averaging window before it's seen as a reliable
|
||||
// average. This prevents erroneous results when the averaging window is
|
||||
// initially being populated for example. The suppression argument disables
|
||||
// further recognitions for a set time after one has been triggered, which can
|
||||
// help reduce spurious recognitions.
|
||||
explicit RecognizeCommands(tflite::ErrorReporter* error_reporter,
|
||||
int32_t average_window_duration_ms = 1000,
|
||||
uint8_t detection_threshold = 200,
|
||||
int32_t suppression_ms = 1500,
|
||||
int32_t minimum_count = 3);
|
||||
|
||||
// Call this with the results of running a model on sample data.
|
||||
TfLiteStatus ProcessLatestResults(const TfLiteTensor* latest_results,
|
||||
const int32_t current_time_ms,
|
||||
const char** found_command, uint8_t* score,
|
||||
bool* is_new_command);
|
||||
|
||||
private:
|
||||
// Configuration
|
||||
tflite::ErrorReporter* error_reporter_;
|
||||
int32_t average_window_duration_ms_;
|
||||
uint8_t detection_threshold_;
|
||||
int32_t suppression_ms_;
|
||||
int32_t minimum_count_;
|
||||
|
||||
// Working variables
|
||||
PreviousResultsQueue previous_results_;
|
||||
const char* previous_top_label_;
|
||||
int32_t previous_top_label_time_;
|
||||
};
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_STATIC_ALLOC_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_STATIC_ALLOC_H_
|
||||
|
||||
// Checks to ensure that the C-style array passed in has a compile-time size of
|
||||
// at least the number of bytes requested. This doesn't work with raw pointers
|
||||
// since sizeof() doesn't know their actual length, so only use this to check
|
||||
// statically-allocated arrays with known sizes.
|
||||
#define STATIC_ALLOC_ENSURE_ARRAY_SIZE(A, N) \
|
||||
do { \
|
||||
if (sizeof(A) < (N)) { \
|
||||
error_reporter->Report(#A " too small (%d bytes, wanted %d) at %s:%d", \
|
||||
sizeof(A), (N), __FILE__, __LINE__); \
|
||||
return 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_STATIC_ALLOC_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// This is a standard TensorFlow Lite model file that has been converted into a
|
||||
// C data array, so it can be easily compiled into a binary for devices that
|
||||
// don't have a file system. It was created using the command:
|
||||
// xxd -i tiny_conv.tflite > tiny_conv_simple_features_model_data.cc
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_TINY_CONV_MICRO_FEATURES_MODEL_DATA_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_TINY_CONV_MICRO_FEATURES_MODEL_DATA_H_
|
||||
|
||||
extern const unsigned char g_tiny_conv_micro_features_model_data[];
|
||||
extern const int g_tiny_conv_micro_features_model_data_len;
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_TINY_CONV_MICRO_FEATURES_MODEL_DATA_H_
|
||||
@@ -0,0 +1,165 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "yes_micro_features_data.h"
|
||||
|
||||
/* File automatically created by
|
||||
* tensorflow/examples/speech_commands/wav_to_features.py \
|
||||
* --sample_rate=16000 \
|
||||
* --clip_duration_ms=1000 \
|
||||
* --window_size_ms=30 \
|
||||
* --window_stride_ms=20 \
|
||||
* --feature_bin_count=40 \
|
||||
* --quantize=1 \
|
||||
* --preprocess="micro" \
|
||||
* --input_wav="speech_commands_test_set_v0.02/yes/f2e59fea_nohash_1.wav" \
|
||||
* --output_c_file="yes_micro_features_data.cc" \
|
||||
*/
|
||||
|
||||
const int g_yes_micro_f2e59fea_nohash_1_width = 40;
|
||||
const int g_yes_micro_f2e59fea_nohash_1_height = 49;
|
||||
const unsigned char g_yes_micro_f2e59fea_nohash_1_data[] = {
|
||||
244, 226, 245, 223, 234, 213, 228, 208, 194, 110, 95, 116, 102, 0, 137,
|
||||
161, 183, 173, 137, 116, 133, 157, 151, 156, 128, 110, 128, 0, 68, 78,
|
||||
78, 90, 68, 68, 78, 102, 95, 78, 95, 78, 210, 188, 209, 183, 204,
|
||||
188, 201, 191, 166, 119, 90, 107, 110, 107, 175, 157, 179, 168, 182, 145,
|
||||
152, 164, 171, 165, 136, 143, 122, 68, 0, 78, 90, 90, 110, 90, 102,
|
||||
99, 90, 68, 78, 68, 223, 186, 179, 123, 182, 110, 196, 171, 159, 110,
|
||||
102, 95, 90, 99, 160, 134, 125, 136, 153, 152, 164, 134, 164, 151, 141,
|
||||
136, 99, 90, 90, 90, 78, 78, 102, 119, 102, 90, 110, 90, 68, 51,
|
||||
177, 175, 211, 172, 183, 0, 95, 68, 129, 102, 68, 85, 114, 105, 110,
|
||||
85, 102, 95, 140, 51, 85, 51, 95, 90, 143, 116, 90, 78, 78, 51,
|
||||
107, 85, 68, 0, 68, 51, 90, 51, 68, 0, 164, 117, 193, 120, 156,
|
||||
0, 138, 51, 90, 0, 51, 0, 51, 85, 0, 0, 51, 0, 0, 0,
|
||||
0, 0, 114, 0, 85, 78, 90, 51, 0, 0, 51, 85, 99, 85, 107,
|
||||
68, 90, 85, 78, 0, 51, 0, 110, 0, 68, 0, 0, 0, 51, 0,
|
||||
51, 0, 0, 0, 68, 90, 107, 0, 68, 0, 0, 0, 68, 0, 51,
|
||||
68, 0, 78, 68, 0, 51, 0, 78, 68, 90, 68, 78, 51, 51, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0,
|
||||
0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 68,
|
||||
0, 0, 78, 0, 78, 0, 78, 0, 51, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 51, 0, 51, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 51,
|
||||
0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
|
||||
0, 0, 0, 0, 51, 78, 0, 0, 51, 51, 0, 0, 0, 78, 0,
|
||||
213, 170, 192, 180, 196, 188, 173, 131, 173, 116, 137, 105, 159, 127, 0,
|
||||
0, 0, 0, 127, 164, 165, 161, 170, 164, 185, 197, 195, 167, 134, 138,
|
||||
159, 134, 136, 105, 51, 0, 99, 0, 51, 0, 228, 215, 229, 218, 237,
|
||||
215, 228, 210, 237, 222, 239, 211, 208, 211, 234, 218, 220, 209, 225, 219,
|
||||
235, 222, 245, 225, 245, 224, 243, 223, 241, 218, 237, 224, 234, 213, 221,
|
||||
193, 197, 164, 157, 128, 227, 188, 232, 196, 220, 220, 240, 219, 234, 213,
|
||||
234, 211, 231, 218, 233, 213, 239, 215, 228, 207, 229, 206, 224, 208, 226,
|
||||
207, 232, 210, 225, 208, 230, 199, 227, 206, 210, 205, 218, 174, 178, 141,
|
||||
235, 208, 220, 206, 225, 203, 233, 203, 225, 167, 205, 199, 208, 190, 221,
|
||||
204, 223, 207, 225, 188, 225, 197, 215, 188, 199, 183, 225, 195, 224, 200,
|
||||
216, 178, 208, 188, 215, 202, 214, 183, 176, 140, 198, 150, 211, 194, 203,
|
||||
120, 175, 188, 204, 189, 219, 192, 223, 202, 216, 186, 203, 185, 210, 182,
|
||||
214, 183, 204, 170, 204, 125, 184, 187, 206, 185, 198, 182, 210, 161, 202,
|
||||
198, 218, 173, 145, 120, 188, 183, 205, 168, 200, 170, 210, 177, 187, 190,
|
||||
209, 193, 193, 166, 210, 162, 175, 119, 174, 147, 182, 161, 181, 134, 176,
|
||||
143, 187, 165, 186, 149, 185, 141, 192, 181, 202, 123, 170, 143, 144, 78,
|
||||
149, 0, 208, 182, 170, 78, 170, 0, 117, 51, 156, 99, 195, 170, 200,
|
||||
130, 152, 68, 175, 141, 173, 134, 194, 132, 189, 164, 198, 134, 173, 117,
|
||||
171, 149, 183, 181, 185, 99, 153, 117, 125, 0, 166, 0, 173, 117, 144,
|
||||
0, 117, 102, 188, 120, 193, 166, 197, 68, 163, 119, 169, 99, 134, 0,
|
||||
162, 0, 164, 68, 171, 116, 126, 0, 120, 68, 68, 0, 105, 0, 159,
|
||||
95, 150, 51, 90, 85, 0, 0, 131, 0, 105, 0, 145, 51, 170, 51,
|
||||
120, 0, 107, 0, 145, 85, 160, 0, 85, 0, 0, 51, 149, 0, 78,
|
||||
0, 0, 0, 0, 0, 0, 0, 90, 0, 112, 0, 78, 102, 122, 0,
|
||||
0, 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112,
|
||||
0, 164, 120, 143, 0, 0, 0, 0, 0, 51, 0, 90, 0, 78, 0,
|
||||
0, 0, 0, 0, 110, 0, 139, 0, 112, 51, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 102, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, 51, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 127, 110, 133, 0, 167, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 0, 190,
|
||||
194, 202, 0, 197, 187, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
214, 213, 223, 203, 218, 189, 200, 122, 78, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 191, 210, 231, 197, 226, 217, 238, 216, 236, 207,
|
||||
199, 0, 0, 0, 0, 0, 107, 122, 155, 160, 214, 215, 236, 202, 235,
|
||||
203, 225, 191, 203, 188, 199, 194, 212, 127, 51, 0, 174, 188, 219, 196,
|
||||
228, 221, 240, 207, 235, 220, 241, 219, 237, 207, 212, 142, 95, 0, 139,
|
||||
78, 162, 177, 197, 183, 211, 199, 235, 208, 238, 215, 227, 207, 211, 201,
|
||||
224, 213, 226, 192, 213, 170, 223, 205, 234, 221, 245, 225, 242, 220, 245,
|
||||
221, 239, 221, 238, 213, 226, 180, 159, 112, 176, 159, 208, 202, 213, 191,
|
||||
205, 191, 225, 197, 238, 219, 224, 201, 227, 200, 221, 201, 225, 203, 212,
|
||||
195, 229, 210, 228, 210, 239, 216, 226, 212, 233, 205, 225, 200, 229, 207,
|
||||
222, 151, 147, 119, 179, 185, 230, 218, 223, 192, 202, 136, 205, 177, 223,
|
||||
204, 228, 215, 232, 209, 221, 189, 221, 205, 209, 200, 226, 209, 229, 205,
|
||||
235, 192, 209, 198, 228, 190, 206, 185, 207, 187, 214, 175, 177, 184, 220,
|
||||
195, 214, 207, 230, 184, 205, 159, 208, 184, 189, 169, 224, 213, 219, 199,
|
||||
229, 203, 216, 205, 222, 204, 224, 206, 231, 208, 231, 176, 197, 184, 216,
|
||||
193, 211, 139, 212, 195, 231, 164, 166, 195, 217, 182, 208, 190, 217, 179,
|
||||
205, 68, 182, 119, 195, 168, 182, 136, 204, 179, 193, 158, 182, 140, 188,
|
||||
154, 197, 169, 190, 99, 184, 0, 125, 0, 131, 0, 99, 68, 179, 85,
|
||||
190, 184, 213, 203, 223, 202, 212, 190, 209, 138, 178, 0, 159, 51, 128,
|
||||
51, 105, 0, 139, 51, 179, 125, 185, 114, 171, 128, 175, 132, 181, 174,
|
||||
155, 0, 0, 0, 90, 0, 125, 0, 176, 188, 227, 217, 244, 215, 234,
|
||||
221, 239, 192, 224, 210, 0, 0, 134, 0, 51, 0, 105, 0, 105, 0,
|
||||
143, 90, 192, 119, 175, 147, 141, 51, 184, 110, 85, 0, 0, 0, 0,
|
||||
0, 0, 0, 151, 139, 201, 203, 232, 203, 226, 208, 236, 206, 230, 212,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 0, 119,
|
||||
0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 133,
|
||||
200, 180, 220, 197, 228, 201, 221, 184, 213, 193, 110, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 78, 0, 164, 0, 0, 0, 0, 0, 107, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 164, 202, 182, 224,
|
||||
197, 211, 179, 212, 193, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
85, 0, 150, 0, 85, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 102, 90, 193, 160, 203, 164, 200, 178, 205, 174,
|
||||
116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 114, 123, 0, 114,
|
||||
0, 145, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
102, 68, 199, 170, 195, 180, 208, 176, 200, 164, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 102, 172, 110, 186,
|
||||
167, 185, 147, 189, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 177, 0, 158, 136, 197, 155, 189, 166,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
85, 0, 155, 90, 175, 117, 175, 138, 202, 165, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 139,
|
||||
0, 120, 68, 51, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, 78, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_YES_MICRO_FEATURES_DATA_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_YES_MICRO_FEATURES_DATA_H_
|
||||
|
||||
extern const int g_yes_micro_f2e59fea_nohash_1_width;
|
||||
extern const int g_yes_micro_f2e59fea_nohash_1_height;
|
||||
extern const unsigned char g_yes_micro_f2e59fea_nohash_1_data[];
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_YES_MICRO_FEATURES_DATA_H_
|
||||
@@ -0,0 +1,177 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "audio_provider.h"
|
||||
#include "micro_model_settings.h"
|
||||
#include <Arduino.h>
|
||||
#include <driver/i2s.h>
|
||||
|
||||
#define I2S_NUM I2S_NUM_0 // 0 or 1
|
||||
#define I2S_SAMPLE_RATE 16000
|
||||
|
||||
#define I2S_PIN_CLK 26
|
||||
#define I2S_PIN_WS 32
|
||||
#define I2S_PIN_DOUT I2S_PIN_NO_CHANGE
|
||||
#define I2S_PIN_DIN 33
|
||||
|
||||
#define BUFFER_SIZE 512
|
||||
|
||||
void CaptureSamples();
|
||||
extern QueueHandle_t xQueueAudioWave;
|
||||
|
||||
namespace {
|
||||
bool g_is_audio_initialized = false;
|
||||
// An internal buffer able to fit 16x our sample size
|
||||
constexpr int kAudioCaptureBufferSize = BUFFER_SIZE * 16;
|
||||
int16_t g_audio_capture_buffer[kAudioCaptureBufferSize];
|
||||
// A buffer that holds our output
|
||||
int16_t g_audio_output_buffer[kMaxAudioSampleSize];
|
||||
// Mark as volatile so we can check in a while loop to see if
|
||||
// any samples have arrived yet.
|
||||
volatile int32_t g_latest_audio_timestamp = 0;
|
||||
// Our callback buffer for collecting a chunk of data
|
||||
volatile int16_t recording_buffer[BUFFER_SIZE];
|
||||
} // namespace
|
||||
|
||||
void InitI2S()
|
||||
{
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
|
||||
.sample_rate = I2S_SAMPLE_RATE,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_I2S,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.dma_buf_count = 4,
|
||||
.dma_buf_len = 256,
|
||||
.use_apll = false,
|
||||
.tx_desc_auto_clear = false,
|
||||
.fixed_mclk = 0
|
||||
};
|
||||
i2s_pin_config_t pin_config = {
|
||||
.bck_io_num = I2S_PIN_CLK,
|
||||
.ws_io_num = I2S_PIN_WS,
|
||||
.data_out_num = I2S_PIN_DOUT,
|
||||
.data_in_num = I2S_PIN_DIN,
|
||||
};
|
||||
|
||||
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
|
||||
i2s_set_pin(I2S_NUM, &pin_config);
|
||||
i2s_set_clk(I2S_NUM, I2S_SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
|
||||
}
|
||||
|
||||
void AudioRecordingTask(void *pvParameters) {
|
||||
static uint16_t audio_idx = 0;
|
||||
size_t bytes_read;
|
||||
int16_t i2s_data;
|
||||
int16_t sample;
|
||||
|
||||
while (1) {
|
||||
|
||||
if (audio_idx >= BUFFER_SIZE) {
|
||||
xQueueSend(xQueueAudioWave, &sample, 0);
|
||||
CaptureSamples();
|
||||
audio_idx = 0;
|
||||
}
|
||||
|
||||
i2s_read(I2S_NUM_0, &i2s_data, 2, &bytes_read, portMAX_DELAY );
|
||||
|
||||
if (bytes_read > 0) {
|
||||
//sample = (0xfff - (i2s_data & 0xfff)) - 0x800;
|
||||
sample = i2s_data;
|
||||
recording_buffer[audio_idx] = sample;
|
||||
audio_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureSamples() {
|
||||
// This is how many bytes of new data we have each time this is called
|
||||
const int number_of_samples = BUFFER_SIZE;
|
||||
// Calculate what timestamp the last audio sample represents
|
||||
const int32_t time_in_ms =
|
||||
g_latest_audio_timestamp +
|
||||
(number_of_samples / (kAudioSampleFrequency / 1000));
|
||||
// Determine the index, in the history of all samples, of the last sample
|
||||
const int32_t start_sample_offset =
|
||||
g_latest_audio_timestamp * (kAudioSampleFrequency / 1000);
|
||||
// Determine the index of this sample in our ring buffer
|
||||
const int capture_index = start_sample_offset % kAudioCaptureBufferSize;
|
||||
// Read the data to the correct place in our buffer, note 2 bytes per buffer entry
|
||||
memcpy(g_audio_capture_buffer + capture_index, (void *)recording_buffer, BUFFER_SIZE * 2);
|
||||
// This is how we let the outside world know that new audio data has arrived.
|
||||
g_latest_audio_timestamp = time_in_ms;
|
||||
|
||||
//int peak = (max_audio - min_audio);
|
||||
//Serial.printf("peak-to-peak: %6d\n", peak);
|
||||
}
|
||||
|
||||
TfLiteStatus InitAudioRecording(tflite::ErrorReporter* error_reporter) {
|
||||
delay(10);
|
||||
|
||||
InitI2S();
|
||||
|
||||
xTaskCreatePinnedToCore(AudioRecordingTask, "AudioRecordingTask", 2048, NULL, 10, NULL, 0);
|
||||
|
||||
// Block until we have our first audio sample
|
||||
while (!g_latest_audio_timestamp) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter,
|
||||
int start_ms, int duration_ms,
|
||||
int* audio_samples_size, int16_t** audio_samples) {
|
||||
// Set everything up to start receiving audio
|
||||
if (!g_is_audio_initialized) {
|
||||
TfLiteStatus init_status = InitAudioRecording(error_reporter);
|
||||
if (init_status != kTfLiteOk) {
|
||||
return init_status;
|
||||
}
|
||||
g_is_audio_initialized = true;
|
||||
}
|
||||
// This next part should only be called when the main thread notices that the
|
||||
// latest audio sample data timestamp has changed, so that there's new data
|
||||
// in the capture ring buffer. The ring buffer will eventually wrap around and
|
||||
// overwrite the data, but the assumption is that the main thread is checking
|
||||
// often enough and the buffer is large enough that this call will be made
|
||||
// before that happens.
|
||||
|
||||
// Determine the index, in the history of all samples, of the first
|
||||
// sample we want
|
||||
const int start_offset = start_ms * (kAudioSampleFrequency / 1000);
|
||||
// Determine how many samples we want in total
|
||||
const int duration_sample_count =
|
||||
duration_ms * (kAudioSampleFrequency / 1000);
|
||||
for (int i = 0; i < duration_sample_count; ++i) {
|
||||
// For each sample, transform its index in the history of all samples into
|
||||
// its index in g_audio_capture_buffer
|
||||
const int capture_index = (start_offset + i) % kAudioCaptureBufferSize;
|
||||
// Write the sample to the output buffer
|
||||
g_audio_output_buffer[i] = g_audio_capture_buffer[capture_index];
|
||||
}
|
||||
|
||||
// Set pointers to provide access to the audio
|
||||
*audio_samples_size = kMaxAudioSampleSize;
|
||||
*audio_samples = g_audio_output_buffer;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
int32_t LatestAudioTimestamp() {
|
||||
return g_latest_audio_timestamp;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// This is an abstraction around an audio source like a microphone, and is
|
||||
// expected to return 16-bit PCM sample data for a given point in time. The
|
||||
// sample data itself should be used as quickly as possible by the caller, since
|
||||
// to allow memory optimizations there are no guarantees that the samples won't
|
||||
// be overwritten by new data in the future. In practice, implementations should
|
||||
// ensure that there's a reasonable time allowed for clients to access the data
|
||||
// before any reuse.
|
||||
// The reference implementation can have no platform-specific dependencies, so
|
||||
// it just returns an array filled with zeros. For real applications, you should
|
||||
// ensure there's a specialized implementation that accesses hardware APIs.
|
||||
TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter,
|
||||
int start_ms, int duration_ms,
|
||||
int* audio_samples_size, int16_t** audio_samples);
|
||||
|
||||
// Returns the time that audio data was last captured in milliseconds. There's
|
||||
// no contract about what time zero represents, the accuracy, or the granularity
|
||||
// of the result. Subsequent calls will generally not return a lower value, but
|
||||
// even that's not guaranteed if there's an overflow wraparound.
|
||||
// The reference implementation of this function just returns a constantly
|
||||
// incrementing value for each call, since it would need a non-portable platform
|
||||
// call to access time information. For real applications, you'll need to write
|
||||
// your own platform-specific implementation.
|
||||
int32_t LatestAudioTimestamp();
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_
|
||||
@@ -0,0 +1,75 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "command_responder.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
int dispMode = 0;
|
||||
|
||||
void InitResponder() {
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
namespace {
|
||||
enum {
|
||||
COMMAND_SILENCE,
|
||||
COMMAND_UNKNOWN,
|
||||
COMMAND_YES,
|
||||
COMMAND_NO,
|
||||
|
||||
COMMAND_MAX
|
||||
};
|
||||
uint8_t scoreList[COMMAND_MAX];
|
||||
uint8_t lastCommand;
|
||||
int8_t lastCommandTime;
|
||||
}
|
||||
|
||||
void RespondToCommand(tflite::ErrorReporter* error_reporter,
|
||||
int32_t current_time, const char* found_command,
|
||||
uint8_t score, bool is_new_command) {
|
||||
static int32_t last_timestamp = 0;
|
||||
|
||||
// Score List Update
|
||||
uint8_t command = COMMAND_SILENCE;
|
||||
memset(scoreList, 0, sizeof(scoreList));
|
||||
if (strcmp(found_command, "silence") == 0) {
|
||||
command = COMMAND_SILENCE;
|
||||
} else if (strcmp(found_command, "unknown") == 0) {
|
||||
command = COMMAND_UNKNOWN;
|
||||
} else if (strcmp(found_command, "yes") == 0) {
|
||||
command = COMMAND_YES;
|
||||
} else if (strcmp(found_command, "no") == 0) {
|
||||
command = COMMAND_NO;
|
||||
}
|
||||
scoreList[command] = score;
|
||||
|
||||
// New Command
|
||||
if (is_new_command) {
|
||||
lastCommand = command;
|
||||
lastCommandTime = 3;
|
||||
}
|
||||
|
||||
Serial.printf("current_time(%d) found_command(%s) score(%d) is_new_command(%d)\n", current_time, found_command, score, is_new_command);
|
||||
}
|
||||
|
||||
int drawWaveX = 160;
|
||||
int drawWaveMin = 1000;
|
||||
int drawWaveMax = -1000;
|
||||
void drawWave(int16_t value) {
|
||||
}
|
||||
|
||||
void drawInput(uint8_t *uint8) {
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// Provides an interface to take an action based on an audio command.
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_COMMAND_RESPONDER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_COMMAND_RESPONDER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Called every time the results of an audio recognition run are available. The
|
||||
// human-readable name of any recognized command is in the `found_command`
|
||||
// argument, `score` has the numerical confidence, and `is_new_command` is set
|
||||
// if the previous command was different to this one.
|
||||
void RespondToCommand(tflite::ErrorReporter* error_reporter,
|
||||
int32_t current_time, const char* found_command,
|
||||
uint8_t score, bool is_new_command);
|
||||
|
||||
void InitResponder();
|
||||
void drawWave(int16_t value);
|
||||
void drawInput(uint8_t *uint8);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_COMMAND_RESPONDER_H_
|
||||
@@ -0,0 +1,118 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "feature_provider.h"
|
||||
|
||||
#include "audio_provider.h"
|
||||
#include "micro_features_generator.h"
|
||||
#include "micro_model_settings.h"
|
||||
|
||||
FeatureProvider::FeatureProvider(int feature_size, uint8_t* feature_data)
|
||||
: feature_size_(feature_size),
|
||||
feature_data_(feature_data),
|
||||
is_first_run_(true) {
|
||||
// Initialize the feature data to default values.
|
||||
for (int n = 0; n < feature_size_; ++n) {
|
||||
feature_data_[n] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FeatureProvider::~FeatureProvider() {}
|
||||
|
||||
TfLiteStatus FeatureProvider::PopulateFeatureData(
|
||||
tflite::ErrorReporter* error_reporter, int32_t last_time_in_ms,
|
||||
int32_t time_in_ms, int* how_many_new_slices) {
|
||||
if (feature_size_ != kFeatureElementCount) {
|
||||
error_reporter->Report("Requested feature_data_ size %d doesn't match %d",
|
||||
feature_size_, kFeatureElementCount);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Quantize the time into steps as long as each window stride, so we can
|
||||
// figure out which audio data we need to fetch.
|
||||
const int last_step = (last_time_in_ms / kFeatureSliceStrideMs);
|
||||
const int current_step = (time_in_ms / kFeatureSliceStrideMs);
|
||||
|
||||
int slices_needed = current_step - last_step;
|
||||
// If this is the first call, make sure we don't use any cached information.
|
||||
if (is_first_run_) {
|
||||
TfLiteStatus init_status = InitializeMicroFeatures(error_reporter);
|
||||
if (init_status != kTfLiteOk) {
|
||||
return init_status;
|
||||
}
|
||||
is_first_run_ = false;
|
||||
slices_needed = kFeatureSliceCount;
|
||||
}
|
||||
if (slices_needed > kFeatureSliceCount) {
|
||||
slices_needed = kFeatureSliceCount;
|
||||
}
|
||||
*how_many_new_slices = slices_needed;
|
||||
|
||||
const int slices_to_keep = kFeatureSliceCount - slices_needed;
|
||||
const int slices_to_drop = kFeatureSliceCount - slices_to_keep;
|
||||
// If we can avoid recalculating some slices, just move the existing data
|
||||
// up in the spectrogram, to perform something like this:
|
||||
// last time = 80ms current time = 120ms
|
||||
// +-----------+ +-----------+
|
||||
// | data@20ms | --> | data@60ms |
|
||||
// +-----------+ -- +-----------+
|
||||
// | data@40ms | -- --> | data@80ms |
|
||||
// +-----------+ -- -- +-----------+
|
||||
// | data@60ms | -- -- | <empty> |
|
||||
// +-----------+ -- +-----------+
|
||||
// | data@80ms | -- | <empty> |
|
||||
// +-----------+ +-----------+
|
||||
if (slices_to_keep > 0) {
|
||||
for (int dest_slice = 0; dest_slice < slices_to_keep; ++dest_slice) {
|
||||
uint8_t* dest_slice_data =
|
||||
feature_data_ + (dest_slice * kFeatureSliceSize);
|
||||
const int src_slice = dest_slice + slices_to_drop;
|
||||
const uint8_t* src_slice_data =
|
||||
feature_data_ + (src_slice * kFeatureSliceSize);
|
||||
for (int i = 0; i < kFeatureSliceSize; ++i) {
|
||||
dest_slice_data[i] = src_slice_data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Any slices that need to be filled in with feature data have their
|
||||
// appropriate audio data pulled, and features calculated for that slice.
|
||||
if (slices_needed > 0) {
|
||||
for (int new_slice = slices_to_keep; new_slice < kFeatureSliceCount;
|
||||
++new_slice) {
|
||||
const int new_step = (current_step - kFeatureSliceCount + 1) + new_slice;
|
||||
const int32_t slice_start_ms = (new_step * kFeatureSliceStrideMs);
|
||||
int16_t* audio_samples = nullptr;
|
||||
int audio_samples_size = 0;
|
||||
// TODO(petewarden): Fix bug that leads to non-zero slice_start_ms
|
||||
GetAudioSamples(error_reporter, (slice_start_ms > 0 ? slice_start_ms : 0),
|
||||
kFeatureSliceDurationMs, &audio_samples_size,
|
||||
&audio_samples);
|
||||
if (audio_samples_size < kMaxAudioSampleSize) {
|
||||
error_reporter->Report("Audio data size %d too small, want %d",
|
||||
audio_samples_size, kMaxAudioSampleSize);
|
||||
return kTfLiteError;
|
||||
}
|
||||
uint8_t* new_slice_data = feature_data_ + (new_slice * kFeatureSliceSize);
|
||||
size_t num_samples_read;
|
||||
TfLiteStatus generate_status = GenerateMicroFeatures(
|
||||
error_reporter, audio_samples, audio_samples_size, kFeatureSliceSize,
|
||||
new_slice_data, &num_samples_read);
|
||||
if (generate_status != kTfLiteOk) {
|
||||
return generate_status;
|
||||
}
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Binds itself to an area of memory intended to hold the input features for an
|
||||
// audio-recognition neural network model, and fills that data area with the
|
||||
// features representing the current audio input, for example from a microphone.
|
||||
// The audio features themselves are a two-dimensional array, made up of
|
||||
// horizontal slices representing the frequencies at one point in time, stacked
|
||||
// on top of each other to form a spectrogram showing how those frequencies
|
||||
// changed over time.
|
||||
class FeatureProvider {
|
||||
public:
|
||||
// Create the provider, and bind it to an area of memory. This memory should
|
||||
// remain accessible for the lifetime of the provider object, since subsequent
|
||||
// calls will fill it with feature data. The provider does no memory
|
||||
// management of this data.
|
||||
FeatureProvider(int feature_size, uint8_t* feature_data);
|
||||
~FeatureProvider();
|
||||
|
||||
// Fills the feature data with information from audio inputs, and returns how
|
||||
// many feature slices were updated.
|
||||
TfLiteStatus PopulateFeatureData(tflite::ErrorReporter* error_reporter,
|
||||
int32_t last_time_in_ms, int32_t time_in_ms,
|
||||
int* how_many_new_slices);
|
||||
|
||||
private:
|
||||
int feature_size_;
|
||||
uint8_t* feature_data_;
|
||||
// Make sure we don't try to use cached information if this is the first call
|
||||
// into the provider.
|
||||
bool is_first_run_;
|
||||
};
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MAIN_FUNCTIONS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MAIN_FUNCTIONS_H_
|
||||
|
||||
// Initializes all data needed for the example. The name is important, and needs
|
||||
// to be setup() for Arduino compatibility.
|
||||
void setup();
|
||||
|
||||
// Runs one iteration of data gathering and inference. This should be called
|
||||
// repeatedly from the application code. The name needs to be loop() for Arduino
|
||||
// compatibility.
|
||||
void loop();
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MAIN_FUNCTIONS_H_
|
||||
@@ -0,0 +1,102 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "micro_features_generator.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "micro_model_settings.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/frontend_util.h"
|
||||
|
||||
// Configure FFT to output 16 bit fixed point.
|
||||
#define FIXED_POINT 16
|
||||
|
||||
namespace {
|
||||
|
||||
FrontendState g_micro_features_state;
|
||||
bool g_is_first_time = true;
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteStatus InitializeMicroFeatures(tflite::ErrorReporter* error_reporter) {
|
||||
FrontendConfig config;
|
||||
config.window.size_ms = kFeatureSliceDurationMs;
|
||||
config.window.step_size_ms = kFeatureSliceStrideMs;
|
||||
config.noise_reduction.smoothing_bits = 10;
|
||||
config.filterbank.num_channels = kFeatureSliceSize;
|
||||
config.filterbank.lower_band_limit = 125.0;
|
||||
config.filterbank.upper_band_limit = 7500.0;
|
||||
config.noise_reduction.smoothing_bits = 10;
|
||||
config.noise_reduction.even_smoothing = 0.025;
|
||||
config.noise_reduction.odd_smoothing = 0.06;
|
||||
config.noise_reduction.min_signal_remaining = 0.05;
|
||||
config.pcan_gain_control.enable_pcan = 1;
|
||||
config.pcan_gain_control.strength = 0.95;
|
||||
config.pcan_gain_control.offset = 80.0;
|
||||
config.pcan_gain_control.gain_bits = 21;
|
||||
config.log_scale.enable_log = 1;
|
||||
config.log_scale.scale_shift = 6;
|
||||
if (!FrontendPopulateState(&config, &g_micro_features_state,
|
||||
kAudioSampleFrequency)) {
|
||||
error_reporter->Report("FrontendPopulateState() failed");
|
||||
return kTfLiteError;
|
||||
}
|
||||
g_is_first_time = true;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// This is not exposed in any header, and is only used for testing, to ensure
|
||||
// that the state is correctly set up before generating results.
|
||||
void SetMicroFeaturesNoiseEstimates(const uint32_t* estimate_presets) {
|
||||
for (int i = 0; i < g_micro_features_state.filterbank.num_channels; ++i) {
|
||||
g_micro_features_state.noise_reduction.estimate[i] = estimate_presets[i];
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus GenerateMicroFeatures(tflite::ErrorReporter* error_reporter,
|
||||
const int16_t* input, int input_size,
|
||||
int output_size, uint8_t* output,
|
||||
size_t* num_samples_read) {
|
||||
const int16_t* frontend_input;
|
||||
if (g_is_first_time) {
|
||||
frontend_input = input;
|
||||
g_is_first_time = false;
|
||||
} else {
|
||||
frontend_input = input + 160;
|
||||
}
|
||||
FrontendOutput frontend_output = FrontendProcessSamples(
|
||||
&g_micro_features_state, frontend_input, input_size, num_samples_read);
|
||||
|
||||
for (int i = 0; i < frontend_output.size; ++i) {
|
||||
// These scaling values are derived from those used in input_data.py in the
|
||||
// training pipeline.
|
||||
constexpr int32_t value_scale = (10 * 255);
|
||||
constexpr int32_t value_div = (256 * 26);
|
||||
int32_t value =
|
||||
((frontend_output.values[i] * value_scale) + (value_div / 2)) /
|
||||
value_div;
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
}
|
||||
if (value > 255) {
|
||||
value = 255;
|
||||
}
|
||||
output[i] = value;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_FEATURES_GENERATOR_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_FEATURES_GENERATOR_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Sets up any resources needed for the feature generation pipeline.
|
||||
TfLiteStatus InitializeMicroFeatures(tflite::ErrorReporter* error_reporter);
|
||||
|
||||
// Converts audio sample data into a more compact form that's appropriate for
|
||||
// feeding into a neural network.
|
||||
TfLiteStatus GenerateMicroFeatures(tflite::ErrorReporter* error_reporter,
|
||||
const int16_t* input, int input_size,
|
||||
int output_size, uint8_t* output,
|
||||
size_t* num_samples_read);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_FEATURES_GENERATOR_H_
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "micro_model_settings.h"
|
||||
|
||||
const char* kCategoryLabels[kCategoryCount] = {
|
||||
"silence",
|
||||
"unknown",
|
||||
"yes",
|
||||
"no",
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_MODEL_SETTINGS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_MODEL_SETTINGS_H_
|
||||
|
||||
// Keeping these as constant expressions allow us to allocate fixed-sized arrays
|
||||
// on the stack for our working memory.
|
||||
|
||||
// The size of the input time series data we pass to the FFT to produce the
|
||||
// frequency information. This has to be a power of two, and since we're dealing
|
||||
// with 30ms of 16KHz inputs, which means 480 samples, this is the next value.
|
||||
constexpr int kMaxAudioSampleSize = 512;
|
||||
constexpr int kAudioSampleFrequency = 16000;
|
||||
|
||||
// All of these values are derived from the values used during model training,
|
||||
// if you change your model you'll need to update these constants.
|
||||
constexpr int kFeatureSliceSize = 40;
|
||||
constexpr int kFeatureSliceCount = 49;
|
||||
constexpr int kFeatureElementCount = (kFeatureSliceSize * kFeatureSliceCount);
|
||||
constexpr int kFeatureSliceStrideMs = 20;
|
||||
constexpr int kFeatureSliceDurationMs = 30;
|
||||
|
||||
constexpr int kCategoryCount = 4;
|
||||
constexpr int kSilenceIndex = 0;
|
||||
constexpr int kUnknownIndex = 1;
|
||||
extern const char* kCategoryLabels[kCategoryCount];
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_MODEL_SETTINGS_H_
|
||||
@@ -0,0 +1,195 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include <TensorFlowLite_ESP32.h>
|
||||
|
||||
#include "main_functions.h"
|
||||
|
||||
#include "audio_provider.h"
|
||||
#include "command_responder.h"
|
||||
#include "feature_provider.h"
|
||||
#include "micro_model_settings.h"
|
||||
#include "tiny_conv_micro_features_model_data.h"
|
||||
#include "recognize_commands.h"
|
||||
#include "tensorflow/lite/experimental/micro/kernels/micro_ops.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_interpreter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_mutable_op_resolver.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
#include "tensorflow/lite/version.h"
|
||||
|
||||
// Globals, used for compatibility with Arduino-style sketches.
|
||||
namespace {
|
||||
tflite::ErrorReporter* error_reporter = nullptr;
|
||||
const tflite::Model* model = nullptr;
|
||||
tflite::MicroInterpreter* interpreter = nullptr;
|
||||
TfLiteTensor* model_input = nullptr;
|
||||
FeatureProvider* feature_provider = nullptr;
|
||||
RecognizeCommands* recognizer = nullptr;
|
||||
int32_t previous_time = 0;
|
||||
|
||||
// Create an area of memory to use for input, output, and intermediate arrays.
|
||||
// The size of this will depend on the model you're using, and may need to be
|
||||
// determined by experimentation.
|
||||
constexpr int kTensorArenaSize = 10 * 1024;
|
||||
uint8_t tensor_arena[kTensorArenaSize];
|
||||
} // namespace
|
||||
|
||||
QueueHandle_t xQueueAudioWave;
|
||||
#define QueueAudioWaveSize 32
|
||||
|
||||
// The name of this function is important for Arduino compatibility.
|
||||
void setup() {
|
||||
xQueueAudioWave = xQueueCreate(QueueAudioWaveSize, sizeof(int16_t));
|
||||
|
||||
// Set up logging. Google style is to avoid globals or statics because of
|
||||
// lifetime uncertainty, but since this has a trivial destructor it's okay.
|
||||
// NOLINTNEXTLINE(runtime-global-variables)
|
||||
static tflite::MicroErrorReporter micro_error_reporter;
|
||||
error_reporter = µ_error_reporter;
|
||||
|
||||
// Map the model into a usable data structure. This doesn't involve any
|
||||
// copying or parsing, it's a very lightweight operation.
|
||||
model = tflite::GetModel(g_tiny_conv_micro_features_model_data);
|
||||
if (model->version() != TFLITE_SCHEMA_VERSION) {
|
||||
error_reporter->Report(
|
||||
"Model provided is schema version %d not equal "
|
||||
"to supported version %d.",
|
||||
model->version(), TFLITE_SCHEMA_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pull in only the operation implementations we need.
|
||||
// This relies on a complete list of all the ops needed by this graph.
|
||||
// An easier approach is to just use the AllOpsResolver, but this will
|
||||
// incur some penalty in code space for op implementations that are not
|
||||
// needed by this graph.
|
||||
//
|
||||
// tflite::ops::micro::AllOpsResolver resolver;
|
||||
// NOLINTNEXTLINE(runtime-global-variables)
|
||||
static tflite::MicroMutableOpResolver micro_mutable_op_resolver;
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_DEPTHWISE_CONV_2D,
|
||||
tflite::ops::micro::Register_DEPTHWISE_CONV_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_FULLY_CONNECTED,
|
||||
tflite::ops::micro::Register_FULLY_CONNECTED());
|
||||
micro_mutable_op_resolver.AddBuiltin(tflite::BuiltinOperator_SOFTMAX,
|
||||
tflite::ops::micro::Register_SOFTMAX());
|
||||
|
||||
// Build an interpreter to run the model with.
|
||||
static tflite::MicroInterpreter static_interpreter(
|
||||
model, micro_mutable_op_resolver, tensor_arena, kTensorArenaSize,
|
||||
error_reporter);
|
||||
interpreter = &static_interpreter;
|
||||
|
||||
// Allocate memory from the tensor_arena for the model's tensors.
|
||||
TfLiteStatus allocate_status = interpreter->AllocateTensors();
|
||||
if (allocate_status != kTfLiteOk) {
|
||||
error_reporter->Report("AllocateTensors() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get information about the memory area to use for the model's input.
|
||||
model_input = interpreter->input(0);
|
||||
if ((model_input->dims->size != 4) || (model_input->dims->data[0] != 1) ||
|
||||
(model_input->dims->data[1] != kFeatureSliceCount) ||
|
||||
(model_input->dims->data[2] != kFeatureSliceSize) ||
|
||||
(model_input->type != kTfLiteUInt8)) {
|
||||
error_reporter->Report("Bad input tensor parameters in model");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare to access the audio spectrograms from a microphone or other source
|
||||
// that will provide the inputs to the neural network.
|
||||
// NOLINTNEXTLINE(runtime-global-variables)
|
||||
static FeatureProvider static_feature_provider(kFeatureElementCount,
|
||||
model_input->data.uint8);
|
||||
feature_provider = &static_feature_provider;
|
||||
|
||||
static RecognizeCommands static_recognizer(error_reporter);
|
||||
recognizer = &static_recognizer;
|
||||
|
||||
previous_time = 0;
|
||||
|
||||
InitResponder();
|
||||
|
||||
Serial.printf("model_input->name : %s\n", model_input->name);
|
||||
Serial.printf("model_input->type : %d\n", model_input->type);
|
||||
Serial.printf("model_input->bytes : %d\n", model_input->bytes);
|
||||
Serial.printf("model_input->dims->size : %d\n", model_input->dims->size);
|
||||
Serial.printf("model_input->dims->data[0] : %d\n", model_input->dims->data[0]); // 1
|
||||
Serial.printf("model_input->dims->data[1] : %d\n", model_input->dims->data[1]); // kFeatureSliceCount
|
||||
Serial.printf("model_input->dims->data[2] : %d\n", model_input->dims->data[2]); // kFeatureSliceSize
|
||||
}
|
||||
|
||||
// The name of this function is important for Arduino compatibility.
|
||||
void loop() {
|
||||
int16_t wave = 0;
|
||||
for (int i = 0; i < QueueAudioWaveSize; i++) {
|
||||
if (xQueueReceive(xQueueAudioWave, &wave, 0) == pdTRUE) {
|
||||
drawWave(wave);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the spectrogram for the current time.
|
||||
const int32_t current_time = LatestAudioTimestamp();
|
||||
int how_many_new_slices = 0;
|
||||
TfLiteStatus feature_status = feature_provider->PopulateFeatureData(
|
||||
error_reporter, previous_time, current_time, &how_many_new_slices);
|
||||
if (feature_status != kTfLiteOk) {
|
||||
error_reporter->Report("Feature generation failed");
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
previous_time = current_time;
|
||||
// If no new audio samples have been received since last time, don't bother
|
||||
// running the network model.
|
||||
if (how_many_new_slices == 0) {
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Run the model on the spectrogram input and make sure it succeeds.
|
||||
TfLiteStatus invoke_status = interpreter->Invoke();
|
||||
if (invoke_status != kTfLiteOk) {
|
||||
error_reporter->Report("Invoke failed");
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Obtain a pointer to the output tensor
|
||||
TfLiteTensor* output = interpreter->output(0);
|
||||
// Determine whether a command was recognized based on the output of inference
|
||||
const char* found_command = nullptr;
|
||||
uint8_t score = 0;
|
||||
bool is_new_command = false;
|
||||
TfLiteStatus process_status = recognizer->ProcessLatestResults(
|
||||
output, current_time, &found_command, &score, &is_new_command);
|
||||
if (process_status != kTfLiteOk) {
|
||||
error_reporter->Report("RecognizeCommands::ProcessLatestResults() failed");
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
// Do something based on the recognized command. The default implementation
|
||||
// just prints to the error console, but you should replace this with your
|
||||
// own function for a real application.
|
||||
RespondToCommand(error_reporter, current_time, found_command, score,
|
||||
is_new_command);
|
||||
|
||||
drawInput(model_input->data.uint8);
|
||||
|
||||
delay(1);
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "no_micro_features_data.h"
|
||||
|
||||
/* File automatically created by
|
||||
* tensorflow/examples/speech_commands/wav_to_features.py \
|
||||
* --sample_rate=16000 \
|
||||
* --clip_duration_ms=1000 \
|
||||
* --window_size_ms=30 \
|
||||
* --window_stride_ms=20 \
|
||||
* --feature_bin_count=40 \
|
||||
* --quantize=1 \
|
||||
* --preprocess="micro" \
|
||||
* --input_wav="speech_commands_test_set_v0.02/no/f9643d42_nohash_4.wav" \
|
||||
* --output_c_file="/tmp/no_micro_features_data.cc" \
|
||||
*/
|
||||
|
||||
const int g_no_micro_f9643d42_nohash_4_width = 40;
|
||||
const int g_no_micro_f9643d42_nohash_4_height = 49;
|
||||
const unsigned char g_no_micro_f9643d42_nohash_4_data[] = {
|
||||
230, 205, 191, 203, 202, 181, 180, 194, 205, 187, 183, 197, 203, 198, 196,
|
||||
186, 202, 159, 151, 126, 110, 138, 141, 142, 137, 148, 133, 120, 110, 126,
|
||||
117, 110, 117, 116, 137, 134, 95, 116, 123, 110, 184, 144, 183, 189, 197,
|
||||
172, 188, 164, 194, 179, 175, 174, 182, 173, 184, 174, 200, 145, 154, 148,
|
||||
147, 135, 143, 122, 127, 138, 116, 99, 122, 105, 110, 125, 127, 133, 131,
|
||||
123, 116, 119, 127, 114, 193, 176, 185, 170, 175, 146, 166, 167, 185, 185,
|
||||
185, 183, 195, 185, 176, 178, 197, 155, 137, 144, 164, 132, 153, 132, 138,
|
||||
137, 134, 95, 120, 116, 131, 122, 99, 120, 120, 110, 116, 110, 126, 127,
|
||||
128, 159, 187, 119, 178, 187, 197, 167, 199, 184, 180, 165, 194, 176, 144,
|
||||
134, 187, 136, 142, 134, 145, 132, 145, 105, 119, 123, 125, 116, 125, 102,
|
||||
129, 138, 130, 99, 99, 90, 120, 123, 134, 95, 194, 172, 187, 123, 191,
|
||||
179, 195, 182, 201, 137, 167, 142, 185, 161, 187, 146, 167, 152, 154, 107,
|
||||
152, 112, 134, 144, 117, 116, 105, 85, 105, 105, 99, 90, 123, 112, 112,
|
||||
68, 107, 105, 117, 99, 116, 143, 139, 90, 154, 142, 188, 172, 178, 135,
|
||||
175, 149, 177, 110, 173, 160, 169, 162, 173, 119, 132, 110, 85, 85, 117,
|
||||
129, 117, 112, 117, 51, 112, 95, 139, 102, 105, 90, 128, 119, 112, 99,
|
||||
170, 168, 195, 152, 174, 173, 180, 0, 157, 130, 169, 149, 149, 123, 170,
|
||||
130, 170, 133, 159, 102, 134, 90, 85, 105, 126, 119, 130, 90, 78, 68,
|
||||
127, 120, 95, 51, 122, 110, 112, 78, 116, 95, 180, 135, 179, 146, 179,
|
||||
162, 197, 153, 172, 135, 154, 0, 149, 95, 145, 114, 166, 0, 114, 110,
|
||||
145, 107, 114, 90, 136, 68, 95, 95, 95, 85, 116, 99, 116, 0, 95,
|
||||
68, 102, 51, 102, 78, 185, 157, 138, 158, 180, 117, 173, 142, 145, 117,
|
||||
169, 130, 159, 99, 138, 123, 169, 90, 78, 0, 123, 85, 107, 51, 114,
|
||||
102, 95, 0, 116, 85, 119, 95, 95, 68, 85, 51, 116, 68, 102, 78,
|
||||
167, 105, 164, 163, 178, 126, 164, 154, 154, 51, 177, 120, 156, 85, 134,
|
||||
139, 168, 90, 161, 102, 114, 116, 122, 95, 112, 102, 107, 51, 114, 85,
|
||||
119, 78, 114, 90, 102, 51, 102, 51, 114, 99, 177, 68, 152, 102, 184,
|
||||
166, 179, 129, 177, 129, 180, 110, 158, 105, 139, 0, 145, 85, 148, 102,
|
||||
117, 102, 116, 0, 78, 68, 90, 51, 107, 85, 78, 0, 51, 0, 51,
|
||||
0, 95, 51, 107, 68, 180, 117, 90, 0, 138, 0, 187, 146, 119, 140,
|
||||
164, 90, 136, 0, 131, 51, 159, 99, 141, 138, 116, 51, 90, 51, 90,
|
||||
68, 105, 0, 85, 78, 112, 51, 122, 95, 128, 68, 85, 0, 112, 68,
|
||||
147, 126, 178, 146, 171, 130, 190, 147, 188, 123, 170, 78, 132, 0, 130,
|
||||
125, 159, 95, 102, 0, 110, 0, 95, 85, 120, 68, 78, 51, 99, 51,
|
||||
105, 0, 112, 102, 105, 68, 90, 51, 90, 0, 127, 95, 166, 175, 187,
|
||||
133, 135, 0, 171, 139, 132, 128, 140, 51, 126, 107, 161, 0, 95, 51,
|
||||
119, 0, 114, 0, 95, 110, 116, 51, 112, 0, 90, 0, 116, 51, 68,
|
||||
0, 105, 68, 105, 0, 164, 78, 173, 0, 194, 166, 145, 114, 116, 51,
|
||||
107, 122, 151, 0, 156, 102, 148, 51, 122, 95, 129, 0, 85, 0, 127,
|
||||
78, 90, 0, 78, 0, 95, 0, 110, 0, 68, 119, 120, 68, 68, 0,
|
||||
122, 99, 147, 127, 200, 167, 85, 114, 161, 85, 161, 125, 143, 99, 156,
|
||||
85, 147, 68, 99, 0, 107, 102, 132, 51, 112, 68, 95, 78, 99, 0,
|
||||
68, 0, 51, 0, 90, 78, 128, 51, 95, 0, 166, 136, 174, 138, 189,
|
||||
144, 130, 129, 138, 134, 132, 120, 134, 0, 51, 78, 147, 51, 51, 0,
|
||||
51, 0, 78, 0, 68, 68, 95, 78, 90, 0, 0, 0, 68, 0, 90,
|
||||
68, 110, 0, 95, 51, 165, 151, 157, 0, 0, 0, 112, 0, 112, 95,
|
||||
149, 107, 119, 68, 126, 68, 138, 0, 78, 0, 78, 0, 99, 51, 112,
|
||||
0, 102, 0, 78, 51, 85, 0, 0, 0, 78, 0, 95, 0, 95, 78,
|
||||
105, 0, 152, 0, 0, 51, 132, 105, 159, 0, 129, 102, 114, 0, 138,
|
||||
51, 123, 0, 129, 78, 119, 51, 51, 51, 105, 0, 78, 85, 95, 0,
|
||||
85, 0, 0, 0, 85, 0, 78, 0, 0, 0, 172, 142, 141, 0, 137,
|
||||
0, 148, 128, 157, 120, 146, 120, 120, 0, 95, 78, 141, 68, 68, 0,
|
||||
68, 0, 90, 0, 85, 0, 107, 0, 78, 0, 85, 51, 102, 0, 68,
|
||||
78, 68, 0, 51, 0, 125, 0, 141, 51, 102, 138, 175, 51, 120, 51,
|
||||
173, 85, 116, 141, 164, 68, 150, 123, 133, 51, 114, 0, 117, 68, 150,
|
||||
51, 116, 68, 78, 0, 68, 0, 68, 0, 85, 0, 78, 0, 51, 78,
|
||||
155, 90, 161, 0, 132, 99, 123, 78, 107, 0, 134, 90, 95, 0, 78,
|
||||
0, 162, 143, 85, 0, 107, 78, 125, 90, 90, 51, 51, 0, 85, 0,
|
||||
0, 0, 132, 102, 102, 154, 128, 0, 99, 68, 162, 102, 151, 0, 99,
|
||||
51, 147, 141, 156, 0, 112, 120, 158, 127, 145, 139, 187, 171, 135, 138,
|
||||
146, 0, 95, 68, 127, 0, 85, 0, 105, 0, 0, 0, 187, 170, 162,
|
||||
188, 165, 51, 51, 78, 243, 215, 225, 196, 205, 181, 205, 168, 176, 134,
|
||||
157, 110, 126, 114, 133, 139, 193, 163, 159, 116, 160, 126, 122, 127, 171,
|
||||
99, 114, 68, 123, 85, 90, 0, 157, 146, 166, 179, 136, 0, 116, 90,
|
||||
242, 219, 240, 204, 216, 164, 188, 171, 176, 164, 154, 158, 190, 157, 190,
|
||||
141, 182, 177, 169, 128, 172, 145, 105, 129, 157, 90, 78, 51, 119, 68,
|
||||
137, 68, 116, 78, 141, 132, 151, 122, 156, 140, 234, 206, 229, 201, 216,
|
||||
174, 191, 144, 162, 85, 122, 157, 194, 167, 204, 149, 180, 166, 166, 139,
|
||||
122, 133, 156, 126, 145, 85, 128, 0, 99, 51, 145, 0, 126, 51, 166,
|
||||
162, 166, 162, 177, 157, 228, 198, 221, 197, 214, 177, 173, 166, 173, 139,
|
||||
185, 191, 202, 163, 205, 172, 206, 189, 135, 68, 166, 134, 149, 134, 135,
|
||||
90, 127, 107, 175, 90, 136, 117, 135, 140, 172, 167, 166, 149, 177, 152,
|
||||
221, 191, 215, 194, 211, 0, 156, 147, 182, 178, 208, 163, 190, 157, 208,
|
||||
200, 195, 164, 179, 154, 181, 150, 143, 99, 132, 137, 185, 143, 163, 85,
|
||||
51, 107, 132, 134, 164, 127, 167, 159, 175, 141, 216, 195, 223, 211, 238,
|
||||
223, 243, 215, 226, 204, 232, 211, 232, 213, 240, 218, 235, 214, 238, 205,
|
||||
207, 173, 149, 201, 215, 200, 230, 213, 208, 195, 175, 151, 195, 175, 182,
|
||||
163, 235, 217, 218, 190, 211, 191, 215, 191, 217, 220, 241, 215, 229, 206,
|
||||
236, 210, 227, 216, 236, 188, 183, 149, 202, 189, 208, 172, 191, 201, 220,
|
||||
193, 221, 207, 216, 208, 201, 131, 170, 187, 229, 197, 211, 194, 226, 201,
|
||||
205, 184, 206, 177, 221, 210, 226, 184, 204, 197, 218, 198, 212, 209, 213,
|
||||
141, 172, 110, 175, 167, 180, 156, 213, 188, 192, 179, 213, 205, 204, 174,
|
||||
200, 147, 162, 181, 203, 167, 198, 187, 210, 164, 196, 169, 189, 168, 224,
|
||||
198, 213, 204, 198, 195, 230, 211, 221, 197, 208, 0, 0, 0, 85, 90,
|
||||
167, 130, 175, 173, 203, 164, 193, 144, 170, 145, 185, 148, 154, 139, 198,
|
||||
159, 180, 171, 216, 174, 178, 161, 166, 136, 216, 184, 215, 197, 199, 190,
|
||||
228, 195, 208, 51, 117, 0, 0, 0, 0, 0, 140, 51, 135, 154, 188,
|
||||
155, 168, 0, 90, 0, 156, 85, 110, 0, 174, 90, 172, 154, 179, 99,
|
||||
142, 166, 179, 157, 177, 95, 192, 142, 204, 198, 217, 147, 173, 0, 112,
|
||||
0, 0, 0, 0, 0, 0, 0, 110, 0, 107, 0, 160, 0, 148, 95,
|
||||
172, 0, 0, 0, 116, 0, 122, 114, 170, 0, 0, 0, 0, 0, 179,
|
||||
110, 196, 85, 205, 183, 169, 0, 99, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 141, 0, 112, 0, 0, 0, 134, 0, 0, 0, 0,
|
||||
0, 0, 0, 139, 0, 0, 0, 0, 112, 186, 78, 163, 0, 169, 128,
|
||||
174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95,
|
||||
0, 105, 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, 0, 95, 0,
|
||||
0, 0, 0, 0, 0, 0, 119, 0, 164, 78, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 68,
|
||||
117, 0, 0, 0, 0, 0, 0, 0, 148, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
|
||||
0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_NO_MICRO_FEATURES_DATA_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_NO_MICRO_FEATURES_DATA_H_
|
||||
|
||||
extern const int g_no_micro_f9643d42_nohash_4_width;
|
||||
extern const int g_no_micro_f9643d42_nohash_4_height;
|
||||
extern const unsigned char g_no_micro_f9643d42_nohash_4_data[];
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_NO_MICRO_FEATURES_DATA_H_
|
||||
@@ -0,0 +1,139 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "recognize_commands.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
RecognizeCommands::RecognizeCommands(tflite::ErrorReporter* error_reporter,
|
||||
int32_t average_window_duration_ms,
|
||||
uint8_t detection_threshold,
|
||||
int32_t suppression_ms,
|
||||
int32_t minimum_count)
|
||||
: error_reporter_(error_reporter),
|
||||
average_window_duration_ms_(average_window_duration_ms),
|
||||
detection_threshold_(detection_threshold),
|
||||
suppression_ms_(suppression_ms),
|
||||
minimum_count_(minimum_count),
|
||||
previous_results_(error_reporter) {
|
||||
previous_top_label_ = "silence";
|
||||
previous_top_label_time_ = std::numeric_limits<int32_t>::min();
|
||||
}
|
||||
|
||||
TfLiteStatus RecognizeCommands::ProcessLatestResults(
|
||||
const TfLiteTensor* latest_results, const int32_t current_time_ms,
|
||||
const char** found_command, uint8_t* score, bool* is_new_command) {
|
||||
if ((latest_results->dims->size != 2) ||
|
||||
(latest_results->dims->data[0] != 1) ||
|
||||
(latest_results->dims->data[1] != kCategoryCount)) {
|
||||
error_reporter_->Report(
|
||||
"The results for recognition should contain %d elements, but there are "
|
||||
"%d in an %d-dimensional shape",
|
||||
kCategoryCount, latest_results->dims->data[1],
|
||||
latest_results->dims->size);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if (latest_results->type != kTfLiteUInt8) {
|
||||
error_reporter_->Report(
|
||||
"The results for recognition should be uint8 elements, but are %d",
|
||||
latest_results->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if ((!previous_results_.empty()) &&
|
||||
(current_time_ms < previous_results_.front().time_)) {
|
||||
error_reporter_->Report(
|
||||
"Results must be fed in increasing time order, but received a "
|
||||
"timestamp of %d that was earlier than the previous one of %d",
|
||||
current_time_ms, previous_results_.front().time_);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Add the latest results to the head of the queue.
|
||||
previous_results_.push_back({current_time_ms, latest_results->data.uint8});
|
||||
|
||||
// Prune any earlier results that are too old for the averaging window.
|
||||
const int64_t time_limit = current_time_ms - average_window_duration_ms_;
|
||||
while ((!previous_results_.empty()) &&
|
||||
previous_results_.front().time_ < time_limit) {
|
||||
previous_results_.pop_front();
|
||||
}
|
||||
|
||||
// If there are too few results, assume the result will be unreliable and
|
||||
// bail.
|
||||
const int64_t how_many_results = previous_results_.size();
|
||||
const int64_t earliest_time = previous_results_.front().time_;
|
||||
const int64_t samples_duration = current_time_ms - earliest_time;
|
||||
if ((how_many_results < minimum_count_) ||
|
||||
(samples_duration < (average_window_duration_ms_ / 4))) {
|
||||
*found_command = previous_top_label_;
|
||||
*score = 0;
|
||||
*is_new_command = false;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Calculate the average score across all the results in the window.
|
||||
int32_t average_scores[kCategoryCount];
|
||||
for (int offset = 0; offset < previous_results_.size(); ++offset) {
|
||||
PreviousResultsQueue::Result previous_result =
|
||||
previous_results_.from_front(offset);
|
||||
const uint8_t* scores = previous_result.scores_;
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
if (offset == 0) {
|
||||
average_scores[i] = scores[i];
|
||||
} else {
|
||||
average_scores[i] += scores[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
average_scores[i] /= how_many_results;
|
||||
}
|
||||
|
||||
// Find the current highest scoring category.
|
||||
int current_top_index = 0;
|
||||
int32_t current_top_score = 0;
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
if (average_scores[i] > current_top_score) {
|
||||
current_top_score = average_scores[i];
|
||||
current_top_index = i;
|
||||
}
|
||||
}
|
||||
const char* current_top_label = kCategoryLabels[current_top_index];
|
||||
|
||||
// If we've recently had another label trigger, assume one that occurs too
|
||||
// soon afterwards is a bad result.
|
||||
int64_t time_since_last_top;
|
||||
if ((previous_top_label_ == kCategoryLabels[0]) ||
|
||||
(previous_top_label_time_ == std::numeric_limits<int32_t>::min())) {
|
||||
time_since_last_top = std::numeric_limits<int32_t>::max();
|
||||
} else {
|
||||
time_since_last_top = current_time_ms - previous_top_label_time_;
|
||||
}
|
||||
if ((current_top_score > detection_threshold_) &&
|
||||
((current_top_label != previous_top_label_) ||
|
||||
(time_since_last_top > suppression_ms_))) {
|
||||
previous_top_label_ = current_top_label;
|
||||
previous_top_label_time_ = current_time_ms;
|
||||
*is_new_command = true;
|
||||
} else {
|
||||
*is_new_command = false;
|
||||
}
|
||||
*found_command = current_top_label;
|
||||
*score = current_top_score;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "micro_model_settings.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Partial implementation of std::dequeue, just providing the functionality
|
||||
// that's needed to keep a record of previous neural network results over a
|
||||
// short time period, so they can be averaged together to produce a more
|
||||
// accurate overall prediction. This doesn't use any dynamic memory allocation
|
||||
// so it's a better fit for microcontroller applications, but this does mean
|
||||
// there are hard limits on the number of results it can store.
|
||||
class PreviousResultsQueue {
|
||||
public:
|
||||
PreviousResultsQueue(tflite::ErrorReporter* error_reporter)
|
||||
: error_reporter_(error_reporter), front_index_(0), size_(0) {}
|
||||
|
||||
// Data structure that holds an inference result, and the time when it
|
||||
// was recorded.
|
||||
struct Result {
|
||||
Result() : time_(0), scores_() {}
|
||||
Result(int32_t time, uint8_t* scores) : time_(time) {
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
scores_[i] = scores[i];
|
||||
}
|
||||
}
|
||||
int32_t time_;
|
||||
uint8_t scores_[kCategoryCount];
|
||||
};
|
||||
|
||||
int size() { return size_; }
|
||||
bool empty() { return size_ == 0; }
|
||||
Result& front() { return results_[front_index_]; }
|
||||
Result& back() {
|
||||
int back_index = front_index_ + (size_ - 1);
|
||||
if (back_index >= kMaxResults) {
|
||||
back_index -= kMaxResults;
|
||||
}
|
||||
return results_[back_index];
|
||||
}
|
||||
|
||||
void push_back(const Result& entry) {
|
||||
if (size() >= kMaxResults) {
|
||||
error_reporter_->Report(
|
||||
"Couldn't push_back latest result, too many already!");
|
||||
return;
|
||||
}
|
||||
size_ += 1;
|
||||
back() = entry;
|
||||
}
|
||||
|
||||
Result pop_front() {
|
||||
if (size() <= 0) {
|
||||
error_reporter_->Report("Couldn't pop_front result, none present!");
|
||||
return Result();
|
||||
}
|
||||
Result result = front();
|
||||
front_index_ += 1;
|
||||
if (front_index_ >= kMaxResults) {
|
||||
front_index_ = 0;
|
||||
}
|
||||
size_ -= 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Most of the functions are duplicates of dequeue containers, but this
|
||||
// is a helper that makes it easy to iterate through the contents of the
|
||||
// queue.
|
||||
Result& from_front(int offset) {
|
||||
if ((offset < 0) || (offset >= size_)) {
|
||||
error_reporter_->Report("Attempt to read beyond the end of the queue!");
|
||||
offset = size_ - 1;
|
||||
}
|
||||
int index = front_index_ + offset;
|
||||
if (index >= kMaxResults) {
|
||||
index -= kMaxResults;
|
||||
}
|
||||
return results_[index];
|
||||
}
|
||||
|
||||
private:
|
||||
tflite::ErrorReporter* error_reporter_;
|
||||
static constexpr int kMaxResults = 50;
|
||||
Result results_[kMaxResults];
|
||||
|
||||
int front_index_;
|
||||
int size_;
|
||||
};
|
||||
|
||||
// This class is designed to apply a very primitive decoding model on top of the
|
||||
// instantaneous results from running an audio recognition model on a single
|
||||
// window of samples. It applies smoothing over time so that noisy individual
|
||||
// label scores are averaged, increasing the confidence that apparent matches
|
||||
// are real.
|
||||
// To use it, you should create a class object with the configuration you
|
||||
// want, and then feed results from running a TensorFlow model into the
|
||||
// processing method. The timestamp for each subsequent call should be
|
||||
// increasing from the previous, since the class is designed to process a stream
|
||||
// of data over time.
|
||||
class RecognizeCommands {
|
||||
public:
|
||||
// labels should be a list of the strings associated with each one-hot score.
|
||||
// The window duration controls the smoothing. Longer durations will give a
|
||||
// higher confidence that the results are correct, but may miss some commands.
|
||||
// The detection threshold has a similar effect, with high values increasing
|
||||
// the precision at the cost of recall. The minimum count controls how many
|
||||
// results need to be in the averaging window before it's seen as a reliable
|
||||
// average. This prevents erroneous results when the averaging window is
|
||||
// initially being populated for example. The suppression argument disables
|
||||
// further recognitions for a set time after one has been triggered, which can
|
||||
// help reduce spurious recognitions.
|
||||
explicit RecognizeCommands(tflite::ErrorReporter* error_reporter,
|
||||
int32_t average_window_duration_ms = 1000,
|
||||
uint8_t detection_threshold = 200,
|
||||
int32_t suppression_ms = 1500,
|
||||
int32_t minimum_count = 3);
|
||||
|
||||
// Call this with the results of running a model on sample data.
|
||||
TfLiteStatus ProcessLatestResults(const TfLiteTensor* latest_results,
|
||||
const int32_t current_time_ms,
|
||||
const char** found_command, uint8_t* score,
|
||||
bool* is_new_command);
|
||||
|
||||
private:
|
||||
// Configuration
|
||||
tflite::ErrorReporter* error_reporter_;
|
||||
int32_t average_window_duration_ms_;
|
||||
uint8_t detection_threshold_;
|
||||
int32_t suppression_ms_;
|
||||
int32_t minimum_count_;
|
||||
|
||||
// Working variables
|
||||
PreviousResultsQueue previous_results_;
|
||||
const char* previous_top_label_;
|
||||
int32_t previous_top_label_time_;
|
||||
};
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_STATIC_ALLOC_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_STATIC_ALLOC_H_
|
||||
|
||||
// Checks to ensure that the C-style array passed in has a compile-time size of
|
||||
// at least the number of bytes requested. This doesn't work with raw pointers
|
||||
// since sizeof() doesn't know their actual length, so only use this to check
|
||||
// statically-allocated arrays with known sizes.
|
||||
#define STATIC_ALLOC_ENSURE_ARRAY_SIZE(A, N) \
|
||||
do { \
|
||||
if (sizeof(A) < (N)) { \
|
||||
error_reporter->Report(#A " too small (%d bytes, wanted %d) at %s:%d", \
|
||||
sizeof(A), (N), __FILE__, __LINE__); \
|
||||
return 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_STATIC_ALLOC_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// This is a standard TensorFlow Lite model file that has been converted into a
|
||||
// C data array, so it can be easily compiled into a binary for devices that
|
||||
// don't have a file system. It was created using the command:
|
||||
// xxd -i tiny_conv.tflite > tiny_conv_simple_features_model_data.cc
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_TINY_CONV_MICRO_FEATURES_MODEL_DATA_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_TINY_CONV_MICRO_FEATURES_MODEL_DATA_H_
|
||||
|
||||
extern const unsigned char g_tiny_conv_micro_features_model_data[];
|
||||
extern const int g_tiny_conv_micro_features_model_data_len;
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_TINY_CONV_MICRO_FEATURES_MODEL_DATA_H_
|
||||
@@ -0,0 +1,165 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "yes_micro_features_data.h"
|
||||
|
||||
/* File automatically created by
|
||||
* tensorflow/examples/speech_commands/wav_to_features.py \
|
||||
* --sample_rate=16000 \
|
||||
* --clip_duration_ms=1000 \
|
||||
* --window_size_ms=30 \
|
||||
* --window_stride_ms=20 \
|
||||
* --feature_bin_count=40 \
|
||||
* --quantize=1 \
|
||||
* --preprocess="micro" \
|
||||
* --input_wav="speech_commands_test_set_v0.02/yes/f2e59fea_nohash_1.wav" \
|
||||
* --output_c_file="yes_micro_features_data.cc" \
|
||||
*/
|
||||
|
||||
const int g_yes_micro_f2e59fea_nohash_1_width = 40;
|
||||
const int g_yes_micro_f2e59fea_nohash_1_height = 49;
|
||||
const unsigned char g_yes_micro_f2e59fea_nohash_1_data[] = {
|
||||
244, 226, 245, 223, 234, 213, 228, 208, 194, 110, 95, 116, 102, 0, 137,
|
||||
161, 183, 173, 137, 116, 133, 157, 151, 156, 128, 110, 128, 0, 68, 78,
|
||||
78, 90, 68, 68, 78, 102, 95, 78, 95, 78, 210, 188, 209, 183, 204,
|
||||
188, 201, 191, 166, 119, 90, 107, 110, 107, 175, 157, 179, 168, 182, 145,
|
||||
152, 164, 171, 165, 136, 143, 122, 68, 0, 78, 90, 90, 110, 90, 102,
|
||||
99, 90, 68, 78, 68, 223, 186, 179, 123, 182, 110, 196, 171, 159, 110,
|
||||
102, 95, 90, 99, 160, 134, 125, 136, 153, 152, 164, 134, 164, 151, 141,
|
||||
136, 99, 90, 90, 90, 78, 78, 102, 119, 102, 90, 110, 90, 68, 51,
|
||||
177, 175, 211, 172, 183, 0, 95, 68, 129, 102, 68, 85, 114, 105, 110,
|
||||
85, 102, 95, 140, 51, 85, 51, 95, 90, 143, 116, 90, 78, 78, 51,
|
||||
107, 85, 68, 0, 68, 51, 90, 51, 68, 0, 164, 117, 193, 120, 156,
|
||||
0, 138, 51, 90, 0, 51, 0, 51, 85, 0, 0, 51, 0, 0, 0,
|
||||
0, 0, 114, 0, 85, 78, 90, 51, 0, 0, 51, 85, 99, 85, 107,
|
||||
68, 90, 85, 78, 0, 51, 0, 110, 0, 68, 0, 0, 0, 51, 0,
|
||||
51, 0, 0, 0, 68, 90, 107, 0, 68, 0, 0, 0, 68, 0, 51,
|
||||
68, 0, 78, 68, 0, 51, 0, 78, 68, 90, 68, 78, 51, 51, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0,
|
||||
0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 68,
|
||||
0, 0, 78, 0, 78, 0, 78, 0, 51, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 51, 0, 51, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 51,
|
||||
0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
|
||||
0, 0, 0, 0, 51, 78, 0, 0, 51, 51, 0, 0, 0, 78, 0,
|
||||
213, 170, 192, 180, 196, 188, 173, 131, 173, 116, 137, 105, 159, 127, 0,
|
||||
0, 0, 0, 127, 164, 165, 161, 170, 164, 185, 197, 195, 167, 134, 138,
|
||||
159, 134, 136, 105, 51, 0, 99, 0, 51, 0, 228, 215, 229, 218, 237,
|
||||
215, 228, 210, 237, 222, 239, 211, 208, 211, 234, 218, 220, 209, 225, 219,
|
||||
235, 222, 245, 225, 245, 224, 243, 223, 241, 218, 237, 224, 234, 213, 221,
|
||||
193, 197, 164, 157, 128, 227, 188, 232, 196, 220, 220, 240, 219, 234, 213,
|
||||
234, 211, 231, 218, 233, 213, 239, 215, 228, 207, 229, 206, 224, 208, 226,
|
||||
207, 232, 210, 225, 208, 230, 199, 227, 206, 210, 205, 218, 174, 178, 141,
|
||||
235, 208, 220, 206, 225, 203, 233, 203, 225, 167, 205, 199, 208, 190, 221,
|
||||
204, 223, 207, 225, 188, 225, 197, 215, 188, 199, 183, 225, 195, 224, 200,
|
||||
216, 178, 208, 188, 215, 202, 214, 183, 176, 140, 198, 150, 211, 194, 203,
|
||||
120, 175, 188, 204, 189, 219, 192, 223, 202, 216, 186, 203, 185, 210, 182,
|
||||
214, 183, 204, 170, 204, 125, 184, 187, 206, 185, 198, 182, 210, 161, 202,
|
||||
198, 218, 173, 145, 120, 188, 183, 205, 168, 200, 170, 210, 177, 187, 190,
|
||||
209, 193, 193, 166, 210, 162, 175, 119, 174, 147, 182, 161, 181, 134, 176,
|
||||
143, 187, 165, 186, 149, 185, 141, 192, 181, 202, 123, 170, 143, 144, 78,
|
||||
149, 0, 208, 182, 170, 78, 170, 0, 117, 51, 156, 99, 195, 170, 200,
|
||||
130, 152, 68, 175, 141, 173, 134, 194, 132, 189, 164, 198, 134, 173, 117,
|
||||
171, 149, 183, 181, 185, 99, 153, 117, 125, 0, 166, 0, 173, 117, 144,
|
||||
0, 117, 102, 188, 120, 193, 166, 197, 68, 163, 119, 169, 99, 134, 0,
|
||||
162, 0, 164, 68, 171, 116, 126, 0, 120, 68, 68, 0, 105, 0, 159,
|
||||
95, 150, 51, 90, 85, 0, 0, 131, 0, 105, 0, 145, 51, 170, 51,
|
||||
120, 0, 107, 0, 145, 85, 160, 0, 85, 0, 0, 51, 149, 0, 78,
|
||||
0, 0, 0, 0, 0, 0, 0, 90, 0, 112, 0, 78, 102, 122, 0,
|
||||
0, 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112,
|
||||
0, 164, 120, 143, 0, 0, 0, 0, 0, 51, 0, 90, 0, 78, 0,
|
||||
0, 0, 0, 0, 110, 0, 139, 0, 112, 51, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 102, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, 51, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 127, 110, 133, 0, 167, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132, 0, 190,
|
||||
194, 202, 0, 197, 187, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
214, 213, 223, 203, 218, 189, 200, 122, 78, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 191, 210, 231, 197, 226, 217, 238, 216, 236, 207,
|
||||
199, 0, 0, 0, 0, 0, 107, 122, 155, 160, 214, 215, 236, 202, 235,
|
||||
203, 225, 191, 203, 188, 199, 194, 212, 127, 51, 0, 174, 188, 219, 196,
|
||||
228, 221, 240, 207, 235, 220, 241, 219, 237, 207, 212, 142, 95, 0, 139,
|
||||
78, 162, 177, 197, 183, 211, 199, 235, 208, 238, 215, 227, 207, 211, 201,
|
||||
224, 213, 226, 192, 213, 170, 223, 205, 234, 221, 245, 225, 242, 220, 245,
|
||||
221, 239, 221, 238, 213, 226, 180, 159, 112, 176, 159, 208, 202, 213, 191,
|
||||
205, 191, 225, 197, 238, 219, 224, 201, 227, 200, 221, 201, 225, 203, 212,
|
||||
195, 229, 210, 228, 210, 239, 216, 226, 212, 233, 205, 225, 200, 229, 207,
|
||||
222, 151, 147, 119, 179, 185, 230, 218, 223, 192, 202, 136, 205, 177, 223,
|
||||
204, 228, 215, 232, 209, 221, 189, 221, 205, 209, 200, 226, 209, 229, 205,
|
||||
235, 192, 209, 198, 228, 190, 206, 185, 207, 187, 214, 175, 177, 184, 220,
|
||||
195, 214, 207, 230, 184, 205, 159, 208, 184, 189, 169, 224, 213, 219, 199,
|
||||
229, 203, 216, 205, 222, 204, 224, 206, 231, 208, 231, 176, 197, 184, 216,
|
||||
193, 211, 139, 212, 195, 231, 164, 166, 195, 217, 182, 208, 190, 217, 179,
|
||||
205, 68, 182, 119, 195, 168, 182, 136, 204, 179, 193, 158, 182, 140, 188,
|
||||
154, 197, 169, 190, 99, 184, 0, 125, 0, 131, 0, 99, 68, 179, 85,
|
||||
190, 184, 213, 203, 223, 202, 212, 190, 209, 138, 178, 0, 159, 51, 128,
|
||||
51, 105, 0, 139, 51, 179, 125, 185, 114, 171, 128, 175, 132, 181, 174,
|
||||
155, 0, 0, 0, 90, 0, 125, 0, 176, 188, 227, 217, 244, 215, 234,
|
||||
221, 239, 192, 224, 210, 0, 0, 134, 0, 51, 0, 105, 0, 105, 0,
|
||||
143, 90, 192, 119, 175, 147, 141, 51, 184, 110, 85, 0, 0, 0, 0,
|
||||
0, 0, 0, 151, 139, 201, 203, 232, 203, 226, 208, 236, 206, 230, 212,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 0, 119,
|
||||
0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 133,
|
||||
200, 180, 220, 197, 228, 201, 221, 184, 213, 193, 110, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 78, 0, 164, 0, 0, 0, 0, 0, 107, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 164, 202, 182, 224,
|
||||
197, 211, 179, 212, 193, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
85, 0, 150, 0, 85, 0, 95, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 102, 90, 193, 160, 203, 164, 200, 178, 205, 174,
|
||||
116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 114, 123, 0, 114,
|
||||
0, 145, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
102, 68, 199, 170, 195, 180, 208, 176, 200, 164, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 142, 102, 172, 110, 186,
|
||||
167, 185, 147, 189, 154, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 177, 0, 158, 136, 197, 155, 189, 166,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
85, 0, 155, 90, 175, 117, 175, 138, 202, 165, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 139,
|
||||
0, 120, 68, 51, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 0, 78, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_YES_MICRO_FEATURES_DATA_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_YES_MICRO_FEATURES_DATA_H_
|
||||
|
||||
extern const int g_yes_micro_f2e59fea_nohash_1_width;
|
||||
extern const int g_yes_micro_f2e59fea_nohash_1_height;
|
||||
extern const unsigned char g_yes_micro_f2e59fea_nohash_1_data[];
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_YES_MICRO_FEATURES_DATA_H_
|
||||
@@ -0,0 +1,175 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "audio_provider.h"
|
||||
#include "micro_model_settings.h"
|
||||
#include <Arduino.h>
|
||||
#include <M5Stack.h>
|
||||
#include <driver/i2s.h>
|
||||
|
||||
#define BACKLIGHT 32
|
||||
|
||||
#define I2S_NUM I2S_NUM_0 // 0 or 1
|
||||
#define I2S_SAMPLE_RATE 16000
|
||||
#define ADC_INPUT ADC1_GPIO34_CHANNEL // ADC CHANNEL
|
||||
#define ADC_UNIT ADC_UNIT_1 // ADC1 or ADC2
|
||||
|
||||
#define BUFFER_SIZE 512
|
||||
|
||||
void CaptureSamples();
|
||||
extern QueueHandle_t xQueueAudioWave;
|
||||
|
||||
namespace {
|
||||
bool g_is_audio_initialized = false;
|
||||
// An internal buffer able to fit 16x our sample size
|
||||
constexpr int kAudioCaptureBufferSize = BUFFER_SIZE * 16;
|
||||
int16_t g_audio_capture_buffer[kAudioCaptureBufferSize];
|
||||
// A buffer that holds our output
|
||||
int16_t g_audio_output_buffer[kMaxAudioSampleSize];
|
||||
// Mark as volatile so we can check in a while loop to see if
|
||||
// any samples have arrived yet.
|
||||
volatile int32_t g_latest_audio_timestamp = 0;
|
||||
// Our callback buffer for collecting a chunk of data
|
||||
volatile int16_t recording_buffer[BUFFER_SIZE];
|
||||
} // namespace
|
||||
|
||||
void InitI2S()
|
||||
{
|
||||
i2s_config_t i2s_config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
|
||||
.sample_rate = I2S_SAMPLE_RATE,
|
||||
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
|
||||
.channel_format = I2S_CHANNEL_FMT_ALL_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.dma_buf_count = 4,
|
||||
.dma_buf_len = 512,
|
||||
.use_apll = false,
|
||||
.tx_desc_auto_clear = false,
|
||||
.fixed_mclk = 0
|
||||
};
|
||||
|
||||
i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
|
||||
i2s_set_adc_mode(ADC_UNIT_1, ADC_INPUT);
|
||||
i2s_set_clk(I2S_NUM, I2S_SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
|
||||
i2s_adc_enable(I2S_NUM);
|
||||
}
|
||||
|
||||
void AudioRecordingTask(void *pvParameters) {
|
||||
static uint16_t audio_idx = 0;
|
||||
size_t bytes_read;
|
||||
uint16_t i2s_data;
|
||||
int16_t sample;
|
||||
|
||||
while (1) {
|
||||
|
||||
if (audio_idx >= BUFFER_SIZE) {
|
||||
xQueueSend(xQueueAudioWave, &sample, 0);
|
||||
CaptureSamples();
|
||||
audio_idx = 0;
|
||||
}
|
||||
|
||||
i2s_read(I2S_NUM_0, &i2s_data, 2, &bytes_read, portMAX_DELAY );
|
||||
|
||||
if (bytes_read > 0) {
|
||||
sample = (0xfff - (i2s_data & 0xfff)) - 0x800;
|
||||
recording_buffer[audio_idx] = sample;
|
||||
audio_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureSamples() {
|
||||
// This is how many bytes of new data we have each time this is called
|
||||
const int number_of_samples = BUFFER_SIZE;
|
||||
// Calculate what timestamp the last audio sample represents
|
||||
const int32_t time_in_ms =
|
||||
g_latest_audio_timestamp +
|
||||
(number_of_samples / (kAudioSampleFrequency / 1000));
|
||||
// Determine the index, in the history of all samples, of the last sample
|
||||
const int32_t start_sample_offset =
|
||||
g_latest_audio_timestamp * (kAudioSampleFrequency / 1000);
|
||||
// Determine the index of this sample in our ring buffer
|
||||
const int capture_index = start_sample_offset % kAudioCaptureBufferSize;
|
||||
// Read the data to the correct place in our buffer, note 2 bytes per buffer entry
|
||||
memcpy(g_audio_capture_buffer + capture_index, (void *)recording_buffer, BUFFER_SIZE * 2);
|
||||
// This is how we let the outside world know that new audio data has arrived.
|
||||
g_latest_audio_timestamp = time_in_ms;
|
||||
|
||||
//int peak = (max_audio - min_audio);
|
||||
//Serial.printf("peak-to-peak: %6d\n", peak);
|
||||
}
|
||||
|
||||
TfLiteStatus InitAudioRecording(tflite::ErrorReporter* error_reporter) {
|
||||
delay(10);
|
||||
|
||||
pinMode( BACKLIGHT, OUTPUT );
|
||||
digitalWrite( BACKLIGHT, HIGH ); // This gives the least noise
|
||||
ledcDetachPin(25);
|
||||
|
||||
InitI2S();
|
||||
|
||||
xTaskCreatePinnedToCore(AudioRecordingTask, "AudioRecordingTask", 2048, NULL, 10, NULL, 0);
|
||||
|
||||
// Block until we have our first audio sample
|
||||
while (!g_latest_audio_timestamp) {
|
||||
delay(1);
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter,
|
||||
int start_ms, int duration_ms,
|
||||
int* audio_samples_size, int16_t** audio_samples) {
|
||||
// Set everything up to start receiving audio
|
||||
if (!g_is_audio_initialized) {
|
||||
TfLiteStatus init_status = InitAudioRecording(error_reporter);
|
||||
if (init_status != kTfLiteOk) {
|
||||
return init_status;
|
||||
}
|
||||
g_is_audio_initialized = true;
|
||||
}
|
||||
// This next part should only be called when the main thread notices that the
|
||||
// latest audio sample data timestamp has changed, so that there's new data
|
||||
// in the capture ring buffer. The ring buffer will eventually wrap around and
|
||||
// overwrite the data, but the assumption is that the main thread is checking
|
||||
// often enough and the buffer is large enough that this call will be made
|
||||
// before that happens.
|
||||
|
||||
// Determine the index, in the history of all samples, of the first
|
||||
// sample we want
|
||||
const int start_offset = start_ms * (kAudioSampleFrequency / 1000);
|
||||
// Determine how many samples we want in total
|
||||
const int duration_sample_count =
|
||||
duration_ms * (kAudioSampleFrequency / 1000);
|
||||
for (int i = 0; i < duration_sample_count; ++i) {
|
||||
// For each sample, transform its index in the history of all samples into
|
||||
// its index in g_audio_capture_buffer
|
||||
const int capture_index = (start_offset + i) % kAudioCaptureBufferSize;
|
||||
// Write the sample to the output buffer
|
||||
g_audio_output_buffer[i] = g_audio_capture_buffer[capture_index];
|
||||
}
|
||||
|
||||
// Set pointers to provide access to the audio
|
||||
*audio_samples_size = kMaxAudioSampleSize;
|
||||
*audio_samples = g_audio_output_buffer;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
int32_t LatestAudioTimestamp() {
|
||||
return g_latest_audio_timestamp;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// This is an abstraction around an audio source like a microphone, and is
|
||||
// expected to return 16-bit PCM sample data for a given point in time. The
|
||||
// sample data itself should be used as quickly as possible by the caller, since
|
||||
// to allow memory optimizations there are no guarantees that the samples won't
|
||||
// be overwritten by new data in the future. In practice, implementations should
|
||||
// ensure that there's a reasonable time allowed for clients to access the data
|
||||
// before any reuse.
|
||||
// The reference implementation can have no platform-specific dependencies, so
|
||||
// it just returns an array filled with zeros. For real applications, you should
|
||||
// ensure there's a specialized implementation that accesses hardware APIs.
|
||||
TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter,
|
||||
int start_ms, int duration_ms,
|
||||
int* audio_samples_size, int16_t** audio_samples);
|
||||
|
||||
// Returns the time that audio data was last captured in milliseconds. There's
|
||||
// no contract about what time zero represents, the accuracy, or the granularity
|
||||
// of the result. Subsequent calls will generally not return a lower value, but
|
||||
// even that's not guaranteed if there's an overflow wraparound.
|
||||
// The reference implementation of this function just returns a constantly
|
||||
// incrementing value for each call, since it would need a non-portable platform
|
||||
// call to access time information. For real applications, you'll need to write
|
||||
// your own platform-specific implementation.
|
||||
int32_t LatestAudioTimestamp();
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_AUDIO_PROVIDER_H_
|
||||
@@ -0,0 +1,137 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "command_responder.h"
|
||||
|
||||
#include <M5Stack.h>
|
||||
|
||||
void InitResponder() {
|
||||
M5.begin();
|
||||
M5.Lcd.fillScreen(BLACK);
|
||||
M5.Lcd.setTextSize(2);
|
||||
M5.Lcd.setCursor(0, 0);
|
||||
M5.Lcd.setTextColor(YELLOW);
|
||||
M5.Lcd.printf("Micro Speech\n");
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
}
|
||||
|
||||
namespace {
|
||||
enum {
|
||||
COMMAND_SILENCE,
|
||||
COMMAND_UNKNOWN,
|
||||
COMMAND_YES,
|
||||
COMMAND_NO,
|
||||
|
||||
COMMAND_MAX
|
||||
};
|
||||
uint8_t scoreList[COMMAND_MAX];
|
||||
uint8_t lastCommand;
|
||||
int8_t lastCommandTime;
|
||||
}
|
||||
|
||||
void RespondToCommand(tflite::ErrorReporter* error_reporter,
|
||||
int32_t current_time, const char* found_command,
|
||||
uint8_t score, bool is_new_command) {
|
||||
static int32_t last_timestamp = 0;
|
||||
|
||||
// Score List Update
|
||||
uint8_t command = COMMAND_SILENCE;
|
||||
memset(scoreList, 0, sizeof(scoreList));
|
||||
if (strcmp(found_command, "silence") == 0) {
|
||||
command = COMMAND_SILENCE;
|
||||
} else if (strcmp(found_command, "unknown") == 0) {
|
||||
command = COMMAND_UNKNOWN;
|
||||
} else if (strcmp(found_command, "yes") == 0) {
|
||||
command = COMMAND_YES;
|
||||
} else if (strcmp(found_command, "no") == 0) {
|
||||
command = COMMAND_NO;
|
||||
}
|
||||
scoreList[command] = score;
|
||||
|
||||
// New Command
|
||||
if (is_new_command) {
|
||||
lastCommand = command;
|
||||
lastCommandTime = 3;
|
||||
}
|
||||
|
||||
Serial.printf("current_time(%d) found_command(%s) score(%d) is_new_command(%d)\n", current_time, found_command, score, is_new_command);
|
||||
M5.Lcd.setCursor(0, 16);
|
||||
|
||||
if (lastCommand == COMMAND_SILENCE && 0 < lastCommandTime) {
|
||||
M5.Lcd.setTextColor(RED, BLACK);
|
||||
} else {
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
}
|
||||
M5.Lcd.printf("Silence : %3d\n", scoreList[COMMAND_SILENCE]);
|
||||
|
||||
if (lastCommand == COMMAND_UNKNOWN && 0 < lastCommandTime) {
|
||||
M5.Lcd.setTextColor(RED, BLACK);
|
||||
} else {
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
}
|
||||
M5.Lcd.printf("Unknown : %3d\n", scoreList[COMMAND_UNKNOWN]);
|
||||
|
||||
if (lastCommand == COMMAND_YES && 0 < lastCommandTime) {
|
||||
M5.Lcd.setTextColor(RED, BLACK);
|
||||
} else {
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
}
|
||||
M5.Lcd.printf("Yes : %3d\n", scoreList[COMMAND_YES]);
|
||||
|
||||
if (lastCommand == COMMAND_NO && 0 < lastCommandTime) {
|
||||
M5.Lcd.setTextColor(RED, BLACK);
|
||||
} else {
|
||||
M5.Lcd.setTextColor(WHITE, BLACK);
|
||||
}
|
||||
M5.Lcd.printf("No : %3d\n", scoreList[COMMAND_NO]);
|
||||
if (0 < lastCommandTime) {
|
||||
lastCommandTime--;
|
||||
}
|
||||
}
|
||||
|
||||
void drawWave(int16_t value) {
|
||||
static int drawX = 320;
|
||||
|
||||
static int min = -1000;
|
||||
static int max = 1000;
|
||||
|
||||
if (value < min) {
|
||||
value = min;
|
||||
}
|
||||
if (max < value) {
|
||||
value = max;
|
||||
}
|
||||
|
||||
int drawY = map(value, min, max, 84, 240);
|
||||
|
||||
M5.Lcd.drawPixel(drawX, drawY, WHITE);
|
||||
drawX++;
|
||||
if (320 <= drawX) {
|
||||
drawX = 0;
|
||||
M5.Lcd.fillRect(0, 84, 320, 240-84, BLUE);
|
||||
}
|
||||
}
|
||||
|
||||
void drawInput(uint8_t *uint8) {
|
||||
for (int y = 0; y < 49; y++) {
|
||||
for (int x = 0; x < 40; x++) {
|
||||
int pos = y * 40 + x;
|
||||
int drawX = 160 + y * 3;
|
||||
int drawY = 80 - x * 2;
|
||||
int color = (uint8[pos]>>2) << 5;
|
||||
M5.Lcd.fillRect(drawX, drawY, 3, 2, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
// Provides an interface to take an action based on an audio command.
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_COMMAND_RESPONDER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_COMMAND_RESPONDER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Called every time the results of an audio recognition run are available. The
|
||||
// human-readable name of any recognized command is in the `found_command`
|
||||
// argument, `score` has the numerical confidence, and `is_new_command` is set
|
||||
// if the previous command was different to this one.
|
||||
void RespondToCommand(tflite::ErrorReporter* error_reporter,
|
||||
int32_t current_time, const char* found_command,
|
||||
uint8_t score, bool is_new_command);
|
||||
|
||||
void InitResponder();
|
||||
void drawWave(int16_t value);
|
||||
void drawInput(uint8_t *uint8);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_COMMAND_RESPONDER_H_
|
||||
@@ -0,0 +1,118 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "feature_provider.h"
|
||||
|
||||
#include "audio_provider.h"
|
||||
#include "micro_features_generator.h"
|
||||
#include "micro_model_settings.h"
|
||||
|
||||
FeatureProvider::FeatureProvider(int feature_size, uint8_t* feature_data)
|
||||
: feature_size_(feature_size),
|
||||
feature_data_(feature_data),
|
||||
is_first_run_(true) {
|
||||
// Initialize the feature data to default values.
|
||||
for (int n = 0; n < feature_size_; ++n) {
|
||||
feature_data_[n] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FeatureProvider::~FeatureProvider() {}
|
||||
|
||||
TfLiteStatus FeatureProvider::PopulateFeatureData(
|
||||
tflite::ErrorReporter* error_reporter, int32_t last_time_in_ms,
|
||||
int32_t time_in_ms, int* how_many_new_slices) {
|
||||
if (feature_size_ != kFeatureElementCount) {
|
||||
error_reporter->Report("Requested feature_data_ size %d doesn't match %d",
|
||||
feature_size_, kFeatureElementCount);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Quantize the time into steps as long as each window stride, so we can
|
||||
// figure out which audio data we need to fetch.
|
||||
const int last_step = (last_time_in_ms / kFeatureSliceStrideMs);
|
||||
const int current_step = (time_in_ms / kFeatureSliceStrideMs);
|
||||
|
||||
int slices_needed = current_step - last_step;
|
||||
// If this is the first call, make sure we don't use any cached information.
|
||||
if (is_first_run_) {
|
||||
TfLiteStatus init_status = InitializeMicroFeatures(error_reporter);
|
||||
if (init_status != kTfLiteOk) {
|
||||
return init_status;
|
||||
}
|
||||
is_first_run_ = false;
|
||||
slices_needed = kFeatureSliceCount;
|
||||
}
|
||||
if (slices_needed > kFeatureSliceCount) {
|
||||
slices_needed = kFeatureSliceCount;
|
||||
}
|
||||
*how_many_new_slices = slices_needed;
|
||||
|
||||
const int slices_to_keep = kFeatureSliceCount - slices_needed;
|
||||
const int slices_to_drop = kFeatureSliceCount - slices_to_keep;
|
||||
// If we can avoid recalculating some slices, just move the existing data
|
||||
// up in the spectrogram, to perform something like this:
|
||||
// last time = 80ms current time = 120ms
|
||||
// +-----------+ +-----------+
|
||||
// | data@20ms | --> | data@60ms |
|
||||
// +-----------+ -- +-----------+
|
||||
// | data@40ms | -- --> | data@80ms |
|
||||
// +-----------+ -- -- +-----------+
|
||||
// | data@60ms | -- -- | <empty> |
|
||||
// +-----------+ -- +-----------+
|
||||
// | data@80ms | -- | <empty> |
|
||||
// +-----------+ +-----------+
|
||||
if (slices_to_keep > 0) {
|
||||
for (int dest_slice = 0; dest_slice < slices_to_keep; ++dest_slice) {
|
||||
uint8_t* dest_slice_data =
|
||||
feature_data_ + (dest_slice * kFeatureSliceSize);
|
||||
const int src_slice = dest_slice + slices_to_drop;
|
||||
const uint8_t* src_slice_data =
|
||||
feature_data_ + (src_slice * kFeatureSliceSize);
|
||||
for (int i = 0; i < kFeatureSliceSize; ++i) {
|
||||
dest_slice_data[i] = src_slice_data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Any slices that need to be filled in with feature data have their
|
||||
// appropriate audio data pulled, and features calculated for that slice.
|
||||
if (slices_needed > 0) {
|
||||
for (int new_slice = slices_to_keep; new_slice < kFeatureSliceCount;
|
||||
++new_slice) {
|
||||
const int new_step = (current_step - kFeatureSliceCount + 1) + new_slice;
|
||||
const int32_t slice_start_ms = (new_step * kFeatureSliceStrideMs);
|
||||
int16_t* audio_samples = nullptr;
|
||||
int audio_samples_size = 0;
|
||||
// TODO(petewarden): Fix bug that leads to non-zero slice_start_ms
|
||||
GetAudioSamples(error_reporter, (slice_start_ms > 0 ? slice_start_ms : 0),
|
||||
kFeatureSliceDurationMs, &audio_samples_size,
|
||||
&audio_samples);
|
||||
if (audio_samples_size < kMaxAudioSampleSize) {
|
||||
error_reporter->Report("Audio data size %d too small, want %d",
|
||||
audio_samples_size, kMaxAudioSampleSize);
|
||||
return kTfLiteError;
|
||||
}
|
||||
uint8_t* new_slice_data = feature_data_ + (new_slice * kFeatureSliceSize);
|
||||
size_t num_samples_read;
|
||||
TfLiteStatus generate_status = GenerateMicroFeatures(
|
||||
error_reporter, audio_samples, audio_samples_size, kFeatureSliceSize,
|
||||
new_slice_data, &num_samples_read);
|
||||
if (generate_status != kTfLiteOk) {
|
||||
return generate_status;
|
||||
}
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Binds itself to an area of memory intended to hold the input features for an
|
||||
// audio-recognition neural network model, and fills that data area with the
|
||||
// features representing the current audio input, for example from a microphone.
|
||||
// The audio features themselves are a two-dimensional array, made up of
|
||||
// horizontal slices representing the frequencies at one point in time, stacked
|
||||
// on top of each other to form a spectrogram showing how those frequencies
|
||||
// changed over time.
|
||||
class FeatureProvider {
|
||||
public:
|
||||
// Create the provider, and bind it to an area of memory. This memory should
|
||||
// remain accessible for the lifetime of the provider object, since subsequent
|
||||
// calls will fill it with feature data. The provider does no memory
|
||||
// management of this data.
|
||||
FeatureProvider(int feature_size, uint8_t* feature_data);
|
||||
~FeatureProvider();
|
||||
|
||||
// Fills the feature data with information from audio inputs, and returns how
|
||||
// many feature slices were updated.
|
||||
TfLiteStatus PopulateFeatureData(tflite::ErrorReporter* error_reporter,
|
||||
int32_t last_time_in_ms, int32_t time_in_ms,
|
||||
int* how_many_new_slices);
|
||||
|
||||
private:
|
||||
int feature_size_;
|
||||
uint8_t* feature_data_;
|
||||
// Make sure we don't try to use cached information if this is the first call
|
||||
// into the provider.
|
||||
bool is_first_run_;
|
||||
};
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_FEATURE_PROVIDER_H_
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MAIN_FUNCTIONS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MAIN_FUNCTIONS_H_
|
||||
|
||||
// Initializes all data needed for the example. The name is important, and needs
|
||||
// to be setup() for Arduino compatibility.
|
||||
void setup();
|
||||
|
||||
// Runs one iteration of data gathering and inference. This should be called
|
||||
// repeatedly from the application code. The name needs to be loop() for Arduino
|
||||
// compatibility.
|
||||
void loop();
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MAIN_FUNCTIONS_H_
|
||||
@@ -0,0 +1,102 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "micro_features_generator.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "micro_model_settings.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/frontend_util.h"
|
||||
|
||||
// Configure FFT to output 16 bit fixed point.
|
||||
#define FIXED_POINT 16
|
||||
|
||||
namespace {
|
||||
|
||||
FrontendState g_micro_features_state;
|
||||
bool g_is_first_time = true;
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteStatus InitializeMicroFeatures(tflite::ErrorReporter* error_reporter) {
|
||||
FrontendConfig config;
|
||||
config.window.size_ms = kFeatureSliceDurationMs;
|
||||
config.window.step_size_ms = kFeatureSliceStrideMs;
|
||||
config.noise_reduction.smoothing_bits = 10;
|
||||
config.filterbank.num_channels = kFeatureSliceSize;
|
||||
config.filterbank.lower_band_limit = 125.0;
|
||||
config.filterbank.upper_band_limit = 7500.0;
|
||||
config.noise_reduction.smoothing_bits = 10;
|
||||
config.noise_reduction.even_smoothing = 0.025;
|
||||
config.noise_reduction.odd_smoothing = 0.06;
|
||||
config.noise_reduction.min_signal_remaining = 0.05;
|
||||
config.pcan_gain_control.enable_pcan = 1;
|
||||
config.pcan_gain_control.strength = 0.95;
|
||||
config.pcan_gain_control.offset = 80.0;
|
||||
config.pcan_gain_control.gain_bits = 21;
|
||||
config.log_scale.enable_log = 1;
|
||||
config.log_scale.scale_shift = 6;
|
||||
if (!FrontendPopulateState(&config, &g_micro_features_state,
|
||||
kAudioSampleFrequency)) {
|
||||
error_reporter->Report("FrontendPopulateState() failed");
|
||||
return kTfLiteError;
|
||||
}
|
||||
g_is_first_time = true;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// This is not exposed in any header, and is only used for testing, to ensure
|
||||
// that the state is correctly set up before generating results.
|
||||
void SetMicroFeaturesNoiseEstimates(const uint32_t* estimate_presets) {
|
||||
for (int i = 0; i < g_micro_features_state.filterbank.num_channels; ++i) {
|
||||
g_micro_features_state.noise_reduction.estimate[i] = estimate_presets[i];
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus GenerateMicroFeatures(tflite::ErrorReporter* error_reporter,
|
||||
const int16_t* input, int input_size,
|
||||
int output_size, uint8_t* output,
|
||||
size_t* num_samples_read) {
|
||||
const int16_t* frontend_input;
|
||||
if (g_is_first_time) {
|
||||
frontend_input = input;
|
||||
g_is_first_time = false;
|
||||
} else {
|
||||
frontend_input = input + 160;
|
||||
}
|
||||
FrontendOutput frontend_output = FrontendProcessSamples(
|
||||
&g_micro_features_state, frontend_input, input_size, num_samples_read);
|
||||
|
||||
for (int i = 0; i < frontend_output.size; ++i) {
|
||||
// These scaling values are derived from those used in input_data.py in the
|
||||
// training pipeline.
|
||||
constexpr int32_t value_scale = (10 * 255);
|
||||
constexpr int32_t value_div = (256 * 26);
|
||||
int32_t value =
|
||||
((frontend_output.values[i] * value_scale) + (value_div / 2)) /
|
||||
value_div;
|
||||
if (value < 0) {
|
||||
value = 0;
|
||||
}
|
||||
if (value > 255) {
|
||||
value = 255;
|
||||
}
|
||||
output[i] = value;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_FEATURES_GENERATOR_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_FEATURES_GENERATOR_H_
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Sets up any resources needed for the feature generation pipeline.
|
||||
TfLiteStatus InitializeMicroFeatures(tflite::ErrorReporter* error_reporter);
|
||||
|
||||
// Converts audio sample data into a more compact form that's appropriate for
|
||||
// feeding into a neural network.
|
||||
TfLiteStatus GenerateMicroFeatures(tflite::ErrorReporter* error_reporter,
|
||||
const int16_t* input, int input_size,
|
||||
int output_size, uint8_t* output,
|
||||
size_t* num_samples_read);
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_FEATURES_GENERATOR_H_
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "micro_model_settings.h"
|
||||
|
||||
const char* kCategoryLabels[kCategoryCount] = {
|
||||
"silence",
|
||||
"unknown",
|
||||
"yes",
|
||||
"no",
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_MODEL_SETTINGS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_MODEL_SETTINGS_H_
|
||||
|
||||
// Keeping these as constant expressions allow us to allocate fixed-sized arrays
|
||||
// on the stack for our working memory.
|
||||
|
||||
// The size of the input time series data we pass to the FFT to produce the
|
||||
// frequency information. This has to be a power of two, and since we're dealing
|
||||
// with 30ms of 16KHz inputs, which means 480 samples, this is the next value.
|
||||
constexpr int kMaxAudioSampleSize = 512;
|
||||
constexpr int kAudioSampleFrequency = 16000;
|
||||
|
||||
// All of these values are derived from the values used during model training,
|
||||
// if you change your model you'll need to update these constants.
|
||||
constexpr int kFeatureSliceSize = 40;
|
||||
constexpr int kFeatureSliceCount = 49;
|
||||
constexpr int kFeatureElementCount = (kFeatureSliceSize * kFeatureSliceCount);
|
||||
constexpr int kFeatureSliceStrideMs = 20;
|
||||
constexpr int kFeatureSliceDurationMs = 30;
|
||||
|
||||
constexpr int kCategoryCount = 4;
|
||||
constexpr int kSilenceIndex = 0;
|
||||
constexpr int kUnknownIndex = 1;
|
||||
extern const char* kCategoryLabels[kCategoryCount];
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_MICRO_MODEL_SETTINGS_H_
|
||||
@@ -0,0 +1,196 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include <TensorFlowLite_ESP32.h>
|
||||
|
||||
#include "main_functions.h"
|
||||
|
||||
#include "audio_provider.h"
|
||||
#include "command_responder.h"
|
||||
#include "feature_provider.h"
|
||||
#include "micro_model_settings.h"
|
||||
#include "tiny_conv_micro_features_model_data.h"
|
||||
#include "recognize_commands.h"
|
||||
#include "tensorflow/lite/experimental/micro/kernels/micro_ops.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_interpreter.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_mutable_op_resolver.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
#include "tensorflow/lite/version.h"
|
||||
|
||||
// Globals, used for compatibility with Arduino-style sketches.
|
||||
namespace {
|
||||
tflite::ErrorReporter* error_reporter = nullptr;
|
||||
const tflite::Model* model = nullptr;
|
||||
tflite::MicroInterpreter* interpreter = nullptr;
|
||||
TfLiteTensor* model_input = nullptr;
|
||||
FeatureProvider* feature_provider = nullptr;
|
||||
RecognizeCommands* recognizer = nullptr;
|
||||
int32_t previous_time = 0;
|
||||
|
||||
// Create an area of memory to use for input, output, and intermediate arrays.
|
||||
// The size of this will depend on the model you're using, and may need to be
|
||||
// determined by experimentation.
|
||||
constexpr int kTensorArenaSize = 10 * 1024;
|
||||
uint8_t tensor_arena[kTensorArenaSize];
|
||||
} // namespace
|
||||
|
||||
QueueHandle_t xQueueAudioWave;
|
||||
#define QueueAudioWaveSize 32
|
||||
|
||||
// The name of this function is important for Arduino compatibility.
|
||||
void setup() {
|
||||
xQueueAudioWave = xQueueCreate(QueueAudioWaveSize, sizeof(int16_t));
|
||||
|
||||
// Set up logging. Google style is to avoid globals or statics because of
|
||||
// lifetime uncertainty, but since this has a trivial destructor it's okay.
|
||||
// NOLINTNEXTLINE(runtime-global-variables)
|
||||
static tflite::MicroErrorReporter micro_error_reporter;
|
||||
error_reporter = µ_error_reporter;
|
||||
|
||||
// Map the model into a usable data structure. This doesn't involve any
|
||||
// copying or parsing, it's a very lightweight operation.
|
||||
model = tflite::GetModel(g_tiny_conv_micro_features_model_data);
|
||||
if (model->version() != TFLITE_SCHEMA_VERSION) {
|
||||
error_reporter->Report(
|
||||
"Model provided is schema version %d not equal "
|
||||
"to supported version %d.",
|
||||
model->version(), TFLITE_SCHEMA_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pull in only the operation implementations we need.
|
||||
// This relies on a complete list of all the ops needed by this graph.
|
||||
// An easier approach is to just use the AllOpsResolver, but this will
|
||||
// incur some penalty in code space for op implementations that are not
|
||||
// needed by this graph.
|
||||
//
|
||||
// tflite::ops::micro::AllOpsResolver resolver;
|
||||
// NOLINTNEXTLINE(runtime-global-variables)
|
||||
static tflite::MicroMutableOpResolver micro_mutable_op_resolver;
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_DEPTHWISE_CONV_2D,
|
||||
tflite::ops::micro::Register_DEPTHWISE_CONV_2D());
|
||||
micro_mutable_op_resolver.AddBuiltin(
|
||||
tflite::BuiltinOperator_FULLY_CONNECTED,
|
||||
tflite::ops::micro::Register_FULLY_CONNECTED());
|
||||
micro_mutable_op_resolver.AddBuiltin(tflite::BuiltinOperator_SOFTMAX,
|
||||
tflite::ops::micro::Register_SOFTMAX());
|
||||
|
||||
// Build an interpreter to run the model with.
|
||||
static tflite::MicroInterpreter static_interpreter(
|
||||
model, micro_mutable_op_resolver, tensor_arena, kTensorArenaSize,
|
||||
error_reporter);
|
||||
interpreter = &static_interpreter;
|
||||
|
||||
// Allocate memory from the tensor_arena for the model's tensors.
|
||||
TfLiteStatus allocate_status = interpreter->AllocateTensors();
|
||||
if (allocate_status != kTfLiteOk) {
|
||||
error_reporter->Report("AllocateTensors() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get information about the memory area to use for the model's input.
|
||||
model_input = interpreter->input(0);
|
||||
if ((model_input->dims->size != 4) || (model_input->dims->data[0] != 1) ||
|
||||
(model_input->dims->data[1] != kFeatureSliceCount) ||
|
||||
(model_input->dims->data[2] != kFeatureSliceSize) ||
|
||||
(model_input->type != kTfLiteUInt8)) {
|
||||
error_reporter->Report("Bad input tensor parameters in model");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare to access the audio spectrograms from a microphone or other source
|
||||
// that will provide the inputs to the neural network.
|
||||
// NOLINTNEXTLINE(runtime-global-variables)
|
||||
static FeatureProvider static_feature_provider(kFeatureElementCount,
|
||||
model_input->data.uint8);
|
||||
feature_provider = &static_feature_provider;
|
||||
|
||||
static RecognizeCommands static_recognizer(error_reporter);
|
||||
recognizer = &static_recognizer;
|
||||
|
||||
previous_time = 0;
|
||||
|
||||
InitResponder();
|
||||
|
||||
Serial.printf("model_input->name : %s\n", model_input->name);
|
||||
Serial.printf("model_input->type : %d\n", model_input->type);
|
||||
Serial.printf("model_input->bytes : %d\n", model_input->bytes);
|
||||
Serial.printf("model_input->dims->size : %d\n", model_input->dims->size);
|
||||
Serial.printf("model_input->dims->data[0] : %d\n", model_input->dims->data[0]); // 1
|
||||
Serial.printf("model_input->dims->data[1] : %d\n", model_input->dims->data[1]); // kFeatureSliceCount
|
||||
Serial.printf("model_input->dims->data[2] : %d\n", model_input->dims->data[2]); // kFeatureSliceSize
|
||||
}
|
||||
|
||||
// The name of this function is important for Arduino compatibility.
|
||||
void loop() {
|
||||
|
||||
int16_t wave = 0;
|
||||
for (int i = 0; i < QueueAudioWaveSize; i++) {
|
||||
if (xQueueReceive(xQueueAudioWave, &wave, 0) == pdTRUE) {
|
||||
drawWave(wave);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the spectrogram for the current time.
|
||||
const int32_t current_time = LatestAudioTimestamp();
|
||||
int how_many_new_slices = 0;
|
||||
TfLiteStatus feature_status = feature_provider->PopulateFeatureData(
|
||||
error_reporter, previous_time, current_time, &how_many_new_slices);
|
||||
if (feature_status != kTfLiteOk) {
|
||||
error_reporter->Report("Feature generation failed");
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
previous_time = current_time;
|
||||
// If no new audio samples have been received since last time, don't bother
|
||||
// running the network model.
|
||||
if (how_many_new_slices == 0) {
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Run the model on the spectrogram input and make sure it succeeds.
|
||||
TfLiteStatus invoke_status = interpreter->Invoke();
|
||||
if (invoke_status != kTfLiteOk) {
|
||||
error_reporter->Report("Invoke failed");
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Obtain a pointer to the output tensor
|
||||
TfLiteTensor* output = interpreter->output(0);
|
||||
// Determine whether a command was recognized based on the output of inference
|
||||
const char* found_command = nullptr;
|
||||
uint8_t score = 0;
|
||||
bool is_new_command = false;
|
||||
TfLiteStatus process_status = recognizer->ProcessLatestResults(
|
||||
output, current_time, &found_command, &score, &is_new_command);
|
||||
if (process_status != kTfLiteOk) {
|
||||
error_reporter->Report("RecognizeCommands::ProcessLatestResults() failed");
|
||||
delay(1);
|
||||
return;
|
||||
}
|
||||
// Do something based on the recognized command. The default implementation
|
||||
// just prints to the error console, but you should replace this with your
|
||||
// own function for a real application.
|
||||
RespondToCommand(error_reporter, current_time, found_command, score,
|
||||
is_new_command);
|
||||
|
||||
drawInput(model_input->data.uint8);
|
||||
|
||||
delay(1);
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "no_micro_features_data.h"
|
||||
|
||||
/* File automatically created by
|
||||
* tensorflow/examples/speech_commands/wav_to_features.py \
|
||||
* --sample_rate=16000 \
|
||||
* --clip_duration_ms=1000 \
|
||||
* --window_size_ms=30 \
|
||||
* --window_stride_ms=20 \
|
||||
* --feature_bin_count=40 \
|
||||
* --quantize=1 \
|
||||
* --preprocess="micro" \
|
||||
* --input_wav="speech_commands_test_set_v0.02/no/f9643d42_nohash_4.wav" \
|
||||
* --output_c_file="/tmp/no_micro_features_data.cc" \
|
||||
*/
|
||||
|
||||
const int g_no_micro_f9643d42_nohash_4_width = 40;
|
||||
const int g_no_micro_f9643d42_nohash_4_height = 49;
|
||||
const unsigned char g_no_micro_f9643d42_nohash_4_data[] = {
|
||||
230, 205, 191, 203, 202, 181, 180, 194, 205, 187, 183, 197, 203, 198, 196,
|
||||
186, 202, 159, 151, 126, 110, 138, 141, 142, 137, 148, 133, 120, 110, 126,
|
||||
117, 110, 117, 116, 137, 134, 95, 116, 123, 110, 184, 144, 183, 189, 197,
|
||||
172, 188, 164, 194, 179, 175, 174, 182, 173, 184, 174, 200, 145, 154, 148,
|
||||
147, 135, 143, 122, 127, 138, 116, 99, 122, 105, 110, 125, 127, 133, 131,
|
||||
123, 116, 119, 127, 114, 193, 176, 185, 170, 175, 146, 166, 167, 185, 185,
|
||||
185, 183, 195, 185, 176, 178, 197, 155, 137, 144, 164, 132, 153, 132, 138,
|
||||
137, 134, 95, 120, 116, 131, 122, 99, 120, 120, 110, 116, 110, 126, 127,
|
||||
128, 159, 187, 119, 178, 187, 197, 167, 199, 184, 180, 165, 194, 176, 144,
|
||||
134, 187, 136, 142, 134, 145, 132, 145, 105, 119, 123, 125, 116, 125, 102,
|
||||
129, 138, 130, 99, 99, 90, 120, 123, 134, 95, 194, 172, 187, 123, 191,
|
||||
179, 195, 182, 201, 137, 167, 142, 185, 161, 187, 146, 167, 152, 154, 107,
|
||||
152, 112, 134, 144, 117, 116, 105, 85, 105, 105, 99, 90, 123, 112, 112,
|
||||
68, 107, 105, 117, 99, 116, 143, 139, 90, 154, 142, 188, 172, 178, 135,
|
||||
175, 149, 177, 110, 173, 160, 169, 162, 173, 119, 132, 110, 85, 85, 117,
|
||||
129, 117, 112, 117, 51, 112, 95, 139, 102, 105, 90, 128, 119, 112, 99,
|
||||
170, 168, 195, 152, 174, 173, 180, 0, 157, 130, 169, 149, 149, 123, 170,
|
||||
130, 170, 133, 159, 102, 134, 90, 85, 105, 126, 119, 130, 90, 78, 68,
|
||||
127, 120, 95, 51, 122, 110, 112, 78, 116, 95, 180, 135, 179, 146, 179,
|
||||
162, 197, 153, 172, 135, 154, 0, 149, 95, 145, 114, 166, 0, 114, 110,
|
||||
145, 107, 114, 90, 136, 68, 95, 95, 95, 85, 116, 99, 116, 0, 95,
|
||||
68, 102, 51, 102, 78, 185, 157, 138, 158, 180, 117, 173, 142, 145, 117,
|
||||
169, 130, 159, 99, 138, 123, 169, 90, 78, 0, 123, 85, 107, 51, 114,
|
||||
102, 95, 0, 116, 85, 119, 95, 95, 68, 85, 51, 116, 68, 102, 78,
|
||||
167, 105, 164, 163, 178, 126, 164, 154, 154, 51, 177, 120, 156, 85, 134,
|
||||
139, 168, 90, 161, 102, 114, 116, 122, 95, 112, 102, 107, 51, 114, 85,
|
||||
119, 78, 114, 90, 102, 51, 102, 51, 114, 99, 177, 68, 152, 102, 184,
|
||||
166, 179, 129, 177, 129, 180, 110, 158, 105, 139, 0, 145, 85, 148, 102,
|
||||
117, 102, 116, 0, 78, 68, 90, 51, 107, 85, 78, 0, 51, 0, 51,
|
||||
0, 95, 51, 107, 68, 180, 117, 90, 0, 138, 0, 187, 146, 119, 140,
|
||||
164, 90, 136, 0, 131, 51, 159, 99, 141, 138, 116, 51, 90, 51, 90,
|
||||
68, 105, 0, 85, 78, 112, 51, 122, 95, 128, 68, 85, 0, 112, 68,
|
||||
147, 126, 178, 146, 171, 130, 190, 147, 188, 123, 170, 78, 132, 0, 130,
|
||||
125, 159, 95, 102, 0, 110, 0, 95, 85, 120, 68, 78, 51, 99, 51,
|
||||
105, 0, 112, 102, 105, 68, 90, 51, 90, 0, 127, 95, 166, 175, 187,
|
||||
133, 135, 0, 171, 139, 132, 128, 140, 51, 126, 107, 161, 0, 95, 51,
|
||||
119, 0, 114, 0, 95, 110, 116, 51, 112, 0, 90, 0, 116, 51, 68,
|
||||
0, 105, 68, 105, 0, 164, 78, 173, 0, 194, 166, 145, 114, 116, 51,
|
||||
107, 122, 151, 0, 156, 102, 148, 51, 122, 95, 129, 0, 85, 0, 127,
|
||||
78, 90, 0, 78, 0, 95, 0, 110, 0, 68, 119, 120, 68, 68, 0,
|
||||
122, 99, 147, 127, 200, 167, 85, 114, 161, 85, 161, 125, 143, 99, 156,
|
||||
85, 147, 68, 99, 0, 107, 102, 132, 51, 112, 68, 95, 78, 99, 0,
|
||||
68, 0, 51, 0, 90, 78, 128, 51, 95, 0, 166, 136, 174, 138, 189,
|
||||
144, 130, 129, 138, 134, 132, 120, 134, 0, 51, 78, 147, 51, 51, 0,
|
||||
51, 0, 78, 0, 68, 68, 95, 78, 90, 0, 0, 0, 68, 0, 90,
|
||||
68, 110, 0, 95, 51, 165, 151, 157, 0, 0, 0, 112, 0, 112, 95,
|
||||
149, 107, 119, 68, 126, 68, 138, 0, 78, 0, 78, 0, 99, 51, 112,
|
||||
0, 102, 0, 78, 51, 85, 0, 0, 0, 78, 0, 95, 0, 95, 78,
|
||||
105, 0, 152, 0, 0, 51, 132, 105, 159, 0, 129, 102, 114, 0, 138,
|
||||
51, 123, 0, 129, 78, 119, 51, 51, 51, 105, 0, 78, 85, 95, 0,
|
||||
85, 0, 0, 0, 85, 0, 78, 0, 0, 0, 172, 142, 141, 0, 137,
|
||||
0, 148, 128, 157, 120, 146, 120, 120, 0, 95, 78, 141, 68, 68, 0,
|
||||
68, 0, 90, 0, 85, 0, 107, 0, 78, 0, 85, 51, 102, 0, 68,
|
||||
78, 68, 0, 51, 0, 125, 0, 141, 51, 102, 138, 175, 51, 120, 51,
|
||||
173, 85, 116, 141, 164, 68, 150, 123, 133, 51, 114, 0, 117, 68, 150,
|
||||
51, 116, 68, 78, 0, 68, 0, 68, 0, 85, 0, 78, 0, 51, 78,
|
||||
155, 90, 161, 0, 132, 99, 123, 78, 107, 0, 134, 90, 95, 0, 78,
|
||||
0, 162, 143, 85, 0, 107, 78, 125, 90, 90, 51, 51, 0, 85, 0,
|
||||
0, 0, 132, 102, 102, 154, 128, 0, 99, 68, 162, 102, 151, 0, 99,
|
||||
51, 147, 141, 156, 0, 112, 120, 158, 127, 145, 139, 187, 171, 135, 138,
|
||||
146, 0, 95, 68, 127, 0, 85, 0, 105, 0, 0, 0, 187, 170, 162,
|
||||
188, 165, 51, 51, 78, 243, 215, 225, 196, 205, 181, 205, 168, 176, 134,
|
||||
157, 110, 126, 114, 133, 139, 193, 163, 159, 116, 160, 126, 122, 127, 171,
|
||||
99, 114, 68, 123, 85, 90, 0, 157, 146, 166, 179, 136, 0, 116, 90,
|
||||
242, 219, 240, 204, 216, 164, 188, 171, 176, 164, 154, 158, 190, 157, 190,
|
||||
141, 182, 177, 169, 128, 172, 145, 105, 129, 157, 90, 78, 51, 119, 68,
|
||||
137, 68, 116, 78, 141, 132, 151, 122, 156, 140, 234, 206, 229, 201, 216,
|
||||
174, 191, 144, 162, 85, 122, 157, 194, 167, 204, 149, 180, 166, 166, 139,
|
||||
122, 133, 156, 126, 145, 85, 128, 0, 99, 51, 145, 0, 126, 51, 166,
|
||||
162, 166, 162, 177, 157, 228, 198, 221, 197, 214, 177, 173, 166, 173, 139,
|
||||
185, 191, 202, 163, 205, 172, 206, 189, 135, 68, 166, 134, 149, 134, 135,
|
||||
90, 127, 107, 175, 90, 136, 117, 135, 140, 172, 167, 166, 149, 177, 152,
|
||||
221, 191, 215, 194, 211, 0, 156, 147, 182, 178, 208, 163, 190, 157, 208,
|
||||
200, 195, 164, 179, 154, 181, 150, 143, 99, 132, 137, 185, 143, 163, 85,
|
||||
51, 107, 132, 134, 164, 127, 167, 159, 175, 141, 216, 195, 223, 211, 238,
|
||||
223, 243, 215, 226, 204, 232, 211, 232, 213, 240, 218, 235, 214, 238, 205,
|
||||
207, 173, 149, 201, 215, 200, 230, 213, 208, 195, 175, 151, 195, 175, 182,
|
||||
163, 235, 217, 218, 190, 211, 191, 215, 191, 217, 220, 241, 215, 229, 206,
|
||||
236, 210, 227, 216, 236, 188, 183, 149, 202, 189, 208, 172, 191, 201, 220,
|
||||
193, 221, 207, 216, 208, 201, 131, 170, 187, 229, 197, 211, 194, 226, 201,
|
||||
205, 184, 206, 177, 221, 210, 226, 184, 204, 197, 218, 198, 212, 209, 213,
|
||||
141, 172, 110, 175, 167, 180, 156, 213, 188, 192, 179, 213, 205, 204, 174,
|
||||
200, 147, 162, 181, 203, 167, 198, 187, 210, 164, 196, 169, 189, 168, 224,
|
||||
198, 213, 204, 198, 195, 230, 211, 221, 197, 208, 0, 0, 0, 85, 90,
|
||||
167, 130, 175, 173, 203, 164, 193, 144, 170, 145, 185, 148, 154, 139, 198,
|
||||
159, 180, 171, 216, 174, 178, 161, 166, 136, 216, 184, 215, 197, 199, 190,
|
||||
228, 195, 208, 51, 117, 0, 0, 0, 0, 0, 140, 51, 135, 154, 188,
|
||||
155, 168, 0, 90, 0, 156, 85, 110, 0, 174, 90, 172, 154, 179, 99,
|
||||
142, 166, 179, 157, 177, 95, 192, 142, 204, 198, 217, 147, 173, 0, 112,
|
||||
0, 0, 0, 0, 0, 0, 0, 110, 0, 107, 0, 160, 0, 148, 95,
|
||||
172, 0, 0, 0, 116, 0, 122, 114, 170, 0, 0, 0, 0, 0, 179,
|
||||
110, 196, 85, 205, 183, 169, 0, 99, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 141, 0, 112, 0, 0, 0, 134, 0, 0, 0, 0,
|
||||
0, 0, 0, 139, 0, 0, 0, 0, 112, 186, 78, 163, 0, 169, 128,
|
||||
174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95,
|
||||
0, 105, 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, 0, 95, 0,
|
||||
0, 0, 0, 0, 0, 0, 119, 0, 164, 78, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 68,
|
||||
117, 0, 0, 0, 0, 0, 0, 0, 148, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
|
||||
0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_NO_MICRO_FEATURES_DATA_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_NO_MICRO_FEATURES_DATA_H_
|
||||
|
||||
extern const int g_no_micro_f9643d42_nohash_4_width;
|
||||
extern const int g_no_micro_f9643d42_nohash_4_height;
|
||||
extern const unsigned char g_no_micro_f9643d42_nohash_4_data[];
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_MICRO_FEATURES_NO_MICRO_FEATURES_DATA_H_
|
||||
@@ -0,0 +1,139 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "recognize_commands.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
RecognizeCommands::RecognizeCommands(tflite::ErrorReporter* error_reporter,
|
||||
int32_t average_window_duration_ms,
|
||||
uint8_t detection_threshold,
|
||||
int32_t suppression_ms,
|
||||
int32_t minimum_count)
|
||||
: error_reporter_(error_reporter),
|
||||
average_window_duration_ms_(average_window_duration_ms),
|
||||
detection_threshold_(detection_threshold),
|
||||
suppression_ms_(suppression_ms),
|
||||
minimum_count_(minimum_count),
|
||||
previous_results_(error_reporter) {
|
||||
previous_top_label_ = "silence";
|
||||
previous_top_label_time_ = std::numeric_limits<int32_t>::min();
|
||||
}
|
||||
|
||||
TfLiteStatus RecognizeCommands::ProcessLatestResults(
|
||||
const TfLiteTensor* latest_results, const int32_t current_time_ms,
|
||||
const char** found_command, uint8_t* score, bool* is_new_command) {
|
||||
if ((latest_results->dims->size != 2) ||
|
||||
(latest_results->dims->data[0] != 1) ||
|
||||
(latest_results->dims->data[1] != kCategoryCount)) {
|
||||
error_reporter_->Report(
|
||||
"The results for recognition should contain %d elements, but there are "
|
||||
"%d in an %d-dimensional shape",
|
||||
kCategoryCount, latest_results->dims->data[1],
|
||||
latest_results->dims->size);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if (latest_results->type != kTfLiteUInt8) {
|
||||
error_reporter_->Report(
|
||||
"The results for recognition should be uint8 elements, but are %d",
|
||||
latest_results->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if ((!previous_results_.empty()) &&
|
||||
(current_time_ms < previous_results_.front().time_)) {
|
||||
error_reporter_->Report(
|
||||
"Results must be fed in increasing time order, but received a "
|
||||
"timestamp of %d that was earlier than the previous one of %d",
|
||||
current_time_ms, previous_results_.front().time_);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Add the latest results to the head of the queue.
|
||||
previous_results_.push_back({current_time_ms, latest_results->data.uint8});
|
||||
|
||||
// Prune any earlier results that are too old for the averaging window.
|
||||
const int64_t time_limit = current_time_ms - average_window_duration_ms_;
|
||||
while ((!previous_results_.empty()) &&
|
||||
previous_results_.front().time_ < time_limit) {
|
||||
previous_results_.pop_front();
|
||||
}
|
||||
|
||||
// If there are too few results, assume the result will be unreliable and
|
||||
// bail.
|
||||
const int64_t how_many_results = previous_results_.size();
|
||||
const int64_t earliest_time = previous_results_.front().time_;
|
||||
const int64_t samples_duration = current_time_ms - earliest_time;
|
||||
if ((how_many_results < minimum_count_) ||
|
||||
(samples_duration < (average_window_duration_ms_ / 4))) {
|
||||
*found_command = previous_top_label_;
|
||||
*score = 0;
|
||||
*is_new_command = false;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Calculate the average score across all the results in the window.
|
||||
int32_t average_scores[kCategoryCount];
|
||||
for (int offset = 0; offset < previous_results_.size(); ++offset) {
|
||||
PreviousResultsQueue::Result previous_result =
|
||||
previous_results_.from_front(offset);
|
||||
const uint8_t* scores = previous_result.scores_;
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
if (offset == 0) {
|
||||
average_scores[i] = scores[i];
|
||||
} else {
|
||||
average_scores[i] += scores[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
average_scores[i] /= how_many_results;
|
||||
}
|
||||
|
||||
// Find the current highest scoring category.
|
||||
int current_top_index = 0;
|
||||
int32_t current_top_score = 0;
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
if (average_scores[i] > current_top_score) {
|
||||
current_top_score = average_scores[i];
|
||||
current_top_index = i;
|
||||
}
|
||||
}
|
||||
const char* current_top_label = kCategoryLabels[current_top_index];
|
||||
|
||||
// If we've recently had another label trigger, assume one that occurs too
|
||||
// soon afterwards is a bad result.
|
||||
int64_t time_since_last_top;
|
||||
if ((previous_top_label_ == kCategoryLabels[0]) ||
|
||||
(previous_top_label_time_ == std::numeric_limits<int32_t>::min())) {
|
||||
time_since_last_top = std::numeric_limits<int32_t>::max();
|
||||
} else {
|
||||
time_since_last_top = current_time_ms - previous_top_label_time_;
|
||||
}
|
||||
if ((current_top_score > detection_threshold_) &&
|
||||
((current_top_label != previous_top_label_) ||
|
||||
(time_since_last_top > suppression_ms_))) {
|
||||
previous_top_label_ = current_top_label;
|
||||
previous_top_label_time_ = current_time_ms;
|
||||
*is_new_command = true;
|
||||
} else {
|
||||
*is_new_command = false;
|
||||
}
|
||||
*found_command = current_top_label;
|
||||
*score = current_top_score;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/c_api_internal.h"
|
||||
#include "micro_model_settings.h"
|
||||
#include "tensorflow/lite/experimental/micro/micro_error_reporter.h"
|
||||
|
||||
// Partial implementation of std::dequeue, just providing the functionality
|
||||
// that's needed to keep a record of previous neural network results over a
|
||||
// short time period, so they can be averaged together to produce a more
|
||||
// accurate overall prediction. This doesn't use any dynamic memory allocation
|
||||
// so it's a better fit for microcontroller applications, but this does mean
|
||||
// there are hard limits on the number of results it can store.
|
||||
class PreviousResultsQueue {
|
||||
public:
|
||||
PreviousResultsQueue(tflite::ErrorReporter* error_reporter)
|
||||
: error_reporter_(error_reporter), front_index_(0), size_(0) {}
|
||||
|
||||
// Data structure that holds an inference result, and the time when it
|
||||
// was recorded.
|
||||
struct Result {
|
||||
Result() : time_(0), scores_() {}
|
||||
Result(int32_t time, uint8_t* scores) : time_(time) {
|
||||
for (int i = 0; i < kCategoryCount; ++i) {
|
||||
scores_[i] = scores[i];
|
||||
}
|
||||
}
|
||||
int32_t time_;
|
||||
uint8_t scores_[kCategoryCount];
|
||||
};
|
||||
|
||||
int size() { return size_; }
|
||||
bool empty() { return size_ == 0; }
|
||||
Result& front() { return results_[front_index_]; }
|
||||
Result& back() {
|
||||
int back_index = front_index_ + (size_ - 1);
|
||||
if (back_index >= kMaxResults) {
|
||||
back_index -= kMaxResults;
|
||||
}
|
||||
return results_[back_index];
|
||||
}
|
||||
|
||||
void push_back(const Result& entry) {
|
||||
if (size() >= kMaxResults) {
|
||||
error_reporter_->Report(
|
||||
"Couldn't push_back latest result, too many already!");
|
||||
return;
|
||||
}
|
||||
size_ += 1;
|
||||
back() = entry;
|
||||
}
|
||||
|
||||
Result pop_front() {
|
||||
if (size() <= 0) {
|
||||
error_reporter_->Report("Couldn't pop_front result, none present!");
|
||||
return Result();
|
||||
}
|
||||
Result result = front();
|
||||
front_index_ += 1;
|
||||
if (front_index_ >= kMaxResults) {
|
||||
front_index_ = 0;
|
||||
}
|
||||
size_ -= 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Most of the functions are duplicates of dequeue containers, but this
|
||||
// is a helper that makes it easy to iterate through the contents of the
|
||||
// queue.
|
||||
Result& from_front(int offset) {
|
||||
if ((offset < 0) || (offset >= size_)) {
|
||||
error_reporter_->Report("Attempt to read beyond the end of the queue!");
|
||||
offset = size_ - 1;
|
||||
}
|
||||
int index = front_index_ + offset;
|
||||
if (index >= kMaxResults) {
|
||||
index -= kMaxResults;
|
||||
}
|
||||
return results_[index];
|
||||
}
|
||||
|
||||
private:
|
||||
tflite::ErrorReporter* error_reporter_;
|
||||
static constexpr int kMaxResults = 50;
|
||||
Result results_[kMaxResults];
|
||||
|
||||
int front_index_;
|
||||
int size_;
|
||||
};
|
||||
|
||||
// This class is designed to apply a very primitive decoding model on top of the
|
||||
// instantaneous results from running an audio recognition model on a single
|
||||
// window of samples. It applies smoothing over time so that noisy individual
|
||||
// label scores are averaged, increasing the confidence that apparent matches
|
||||
// are real.
|
||||
// To use it, you should create a class object with the configuration you
|
||||
// want, and then feed results from running a TensorFlow model into the
|
||||
// processing method. The timestamp for each subsequent call should be
|
||||
// increasing from the previous, since the class is designed to process a stream
|
||||
// of data over time.
|
||||
class RecognizeCommands {
|
||||
public:
|
||||
// labels should be a list of the strings associated with each one-hot score.
|
||||
// The window duration controls the smoothing. Longer durations will give a
|
||||
// higher confidence that the results are correct, but may miss some commands.
|
||||
// The detection threshold has a similar effect, with high values increasing
|
||||
// the precision at the cost of recall. The minimum count controls how many
|
||||
// results need to be in the averaging window before it's seen as a reliable
|
||||
// average. This prevents erroneous results when the averaging window is
|
||||
// initially being populated for example. The suppression argument disables
|
||||
// further recognitions for a set time after one has been triggered, which can
|
||||
// help reduce spurious recognitions.
|
||||
explicit RecognizeCommands(tflite::ErrorReporter* error_reporter,
|
||||
int32_t average_window_duration_ms = 1000,
|
||||
uint8_t detection_threshold = 200,
|
||||
int32_t suppression_ms = 1500,
|
||||
int32_t minimum_count = 3);
|
||||
|
||||
// Call this with the results of running a model on sample data.
|
||||
TfLiteStatus ProcessLatestResults(const TfLiteTensor* latest_results,
|
||||
const int32_t current_time_ms,
|
||||
const char** found_command, uint8_t* score,
|
||||
bool* is_new_command);
|
||||
|
||||
private:
|
||||
// Configuration
|
||||
tflite::ErrorReporter* error_reporter_;
|
||||
int32_t average_window_duration_ms_;
|
||||
uint8_t detection_threshold_;
|
||||
int32_t suppression_ms_;
|
||||
int32_t minimum_count_;
|
||||
|
||||
// Working variables
|
||||
PreviousResultsQueue previous_results_;
|
||||
const char* previous_top_label_;
|
||||
int32_t previous_top_label_time_;
|
||||
};
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICRO_EXAMPLES_MICRO_SPEECH_RECOGNIZE_COMMANDS_H_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user