mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-08 12:36:52 +03:00
Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96bb86536f | ||
|
|
2daf6c8b3f | ||
|
|
24b46158de | ||
|
|
63d336ba28 | ||
|
|
8dd3a92671 | ||
|
|
8e8897c70d | ||
|
|
eac513411e | ||
|
|
98f9274085 | ||
|
|
884dd9fc3b | ||
|
|
04c564936a | ||
|
|
957138960a | ||
|
|
58a0297915 | ||
|
|
61bf536207 | ||
|
|
4136a7b372 | ||
|
|
b3afc9961d | ||
|
|
27e2e042da | ||
|
|
cc659bad30 | ||
|
|
776931c0ad | ||
|
|
e22b4b6fe1 | ||
|
|
cad534bffe | ||
|
|
3b44adb6fa | ||
|
|
cc0bd1473f | ||
|
|
58124d27bf | ||
|
|
9ad118814a | ||
|
|
270f8dd093 | ||
|
|
fde0ae4781 | ||
|
|
b87ce8c75d | ||
|
|
e372c87836 | ||
|
|
f8478d7742 | ||
|
|
a3d6923fec | ||
|
|
7bfa22187d | ||
|
|
7a8affae32 | ||
|
|
e48dd7c4c4 | ||
|
|
6d298c1746 | ||
|
|
4fe9ab92e0 | ||
|
|
f2ac4cdc64 | ||
|
|
be902401d1 | ||
|
|
020e93bca2 | ||
|
|
61dfdc2258 | ||
|
|
a98e3c8eab | ||
|
|
58a90ff706 | ||
|
|
d0bf12f3d4 | ||
|
|
af16785bbf | ||
|
|
18f6e83a2c | ||
|
|
147d97421b | ||
|
|
dcf2feb7aa | ||
|
|
e63e940b96 | ||
|
|
68b0fb83ee | ||
|
|
f15e5f060a | ||
|
|
e2a403441f | ||
|
|
9b3665b9c6 | ||
|
|
f4c8bf9206 | ||
|
|
c033db9c31 | ||
|
|
9300526f49 | ||
|
|
b6dd1f7f2d | ||
|
|
1e6eddca04 | ||
|
|
19ca0d7dd7 | ||
|
|
7fcb5d1c0c | ||
|
|
dd995ec28a | ||
|
|
af99de3535 | ||
|
|
3567cc2fb0 | ||
|
|
5e9d9bd264 | ||
|
|
62447c1bb9 | ||
|
|
a86434c9a2 | ||
|
|
b7b70299f7 | ||
|
|
eb02e0aec1 | ||
|
|
7816e53db7 | ||
|
|
7ae08e572a | ||
|
|
47d15d8adb | ||
|
|
0dac0e87e4 | ||
|
|
b290099d5b | ||
|
|
f6b1a41a0b | ||
|
|
e529af04cf | ||
|
|
6c365dd949 | ||
|
|
32f15fc557 | ||
|
|
6f06af1d5f | ||
|
|
a91f99faab | ||
|
|
17a87b23a1 | ||
|
|
d4b5ec2ae2 | ||
|
|
1bcaf09855 | ||
|
|
fa3842b2b4 | ||
|
|
ea72256e56 | ||
|
|
be5828cb3e | ||
|
|
104b72505c | ||
|
|
23728a0686 | ||
|
|
eaaa856b13 |
93
Changelog.md
93
Changelog.md
@@ -2,6 +2,99 @@
|
||||
|
||||
|
||||
|
||||
##### 8.5.0 - Multi Meter Support (2021-10-07)
|
||||
|
||||
* Upgrade digital CNN to v13.1.0 (added new images)
|
||||
* bug fix: wlan password with space, double digit output
|
||||
|
||||
##### 8.4.0 - Multi Meter Support (2021-09-25)
|
||||
|
||||
* License change (remove MIT license, remark see below)
|
||||
|
||||
* html: show hostname in title and main page
|
||||
|
||||
* configuration:
|
||||
|
||||
* moved setting `ExtendedResolution` to individual number settings
|
||||
* New parameter `IgnoreLeadingNaN` (delete leading NaN's specifically)
|
||||
* **ATTENTION**: update of the `config.ini` needed (open, adjust `ExtendedResolution`, save)
|
||||
|
||||
* Bug fixing (html, images of recognized numbers)
|
||||
|
||||
|
||||
|
||||
### **ATTENTION: LICENSE CHANGE - removal of MIT License.**
|
||||
|
||||
- Currently no licence published - copyright belongs to author
|
||||
- If you are interested in a commercial usage or dedicated versions please contact the developer
|
||||
- no limits to private usage
|
||||
|
||||
|
||||
|
||||
##### 8.3.0 - Multi Meter Support (2021-09-12)
|
||||
|
||||
* Upgrade digital CNN to v12.1.0 (added new images)
|
||||
* Dedicated NaN handling, internal refactoring (CNN-Handling)
|
||||
* HTML: confirmation after config.ini update
|
||||
* Bug fixing
|
||||
|
||||
##### 8.2.0 - Multi Meter Support (2021-08-24)
|
||||
|
||||
* Improve server responsiveness
|
||||
* Flow status and prevalue status in overview
|
||||
* Improved prevalue handling
|
||||
|
||||
##### 8.1.0 - Multi Meter Support (2021-08-12)
|
||||
|
||||
* GPIO: using the general mqtt main topic for GPIO
|
||||
|
||||
* Upgrade digital CNN to v12.0.0 (added new images)
|
||||
* Update tfmicro to new master (2021-08-07)
|
||||
* Bug fix: remove text in mqtt value, remove connect limit in wlan reconnet
|
||||
|
||||
##### 8.0.5 - Multi Meter Support (2021-08-01)
|
||||
|
||||
* NEW 8.0.5: bug fix: saving prevalue
|
||||
* NEW 8.0.4: bug fix: load config.ini after upgrade
|
||||
* NEW 8.0.3: bug fix: reboot during `config.ini` handling, html error
|
||||
* NEW 8.0.2: saving roundes prevalue, bug fix html server
|
||||
* NEW 8.0.1: bug fix: html handling of parameter `FixedExposure` and `ImageSize`
|
||||
* Dual / multi meter support (more than 1 number to be recognized)
|
||||
This is implemented with the feature "number" on the ROI definition as well as selected options
|
||||
* MQTT: standardization of the naming - including new topics (`json`, `freeMem `, `uptime`)c
|
||||
* Preparation for extended GPIO support (thanks to Zwerk2k) - not tested and fully functional yet
|
||||
* Bug fixing: html server, memory leak, MQTT connect, hostname, turn of flash LED
|
||||
|
||||
<span style="color: red;">**ATTENTION: the configuration and prevalue files are modified automatically and will not be backward compatible!**</span>
|
||||
|
||||
##### 7.1.2 MQTT-Update - (2021-06-17)
|
||||
|
||||
* NEW: 7.1.2: bug fix setting hostname, Flash-LED not off during reboot
|
||||
|
||||
* NEW: 7.1.1: bug fix wlan password with "=" (again)
|
||||
|
||||
* MQTT error message: changes "no error", send retain flag
|
||||
|
||||
* Update wlan handling to esp-idf 4.1
|
||||
|
||||
* Upgrade digital CNN to v8.7.0 (added new images)
|
||||
|
||||
* Bug fix: MQTT, WLAN, LED-Controll, GPIO usage, fixed IP, calculation flow rate
|
||||
|
||||
|
||||
|
||||
##### 7.0.1 MQTT-Update - (2021-05-13)
|
||||
|
||||
* NEW: 7.0.1: bug fix wlan password with "="
|
||||
|
||||
* Upgrade digital CNN to v8.5.0 (added new images)
|
||||
|
||||
* New MQTT topics: flow rate (units/minute), time stamp (last correct read readout)
|
||||
|
||||
* Update MQTT/Error topic to " " in case no error (instead of empty string)
|
||||
|
||||
* Portrait or landscape image orientation in rotated image (avoid cropping)
|
||||
|
||||
##### 6.7.2 Image Processing in Memory - (2021-05-01)
|
||||
|
||||
* NEW 6.7.2: Updated html for setup modus - remove reboot on edit configuration)
|
||||
|
||||
@@ -11,6 +11,71 @@
|
||||
|
||||
____
|
||||
|
||||
#### #17 Direct InfluxDB connection
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/534
|
||||
* Direct interface to a InfluxDB data base
|
||||
* Integrate InfluxDB interface in firmware
|
||||
* Adapt html web page for configuration
|
||||
|
||||
|
||||
#### #16 Serial Communication
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/512
|
||||
* Send the readout value via RX/TX interface with a dedicated TAG
|
||||
* Make dedicated communication FlowModule
|
||||
* Modification of RX/TX communication
|
||||
* Configuration interfache
|
||||
|
||||
|
||||
#### #15 Calibration for FishEye image
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/507
|
||||
|
||||
1. The development of such a correction algorithm with the libraries, that are available for the ESP32 environment.
|
||||
2. New module for integration of the flow into the image processing flow.
|
||||
3. Extension of the configuration (config.ini) and html-pages
|
||||
4. Parameter adjustment and testing for every different fish-eye module
|
||||
5. Maintenance for further updates / modules, ...
|
||||
|
||||
|
||||
|
||||
#### #14 Backup and restore option for configuration
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/459
|
||||
|
||||
* Implement a zip file compression for store and restore
|
||||
|
||||
* Update the html to handle it
|
||||
|
||||
|
||||
|
||||
#### #13 Manage non linear gauge without CNN re-training
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/443
|
||||
|
||||
* Implement a look up table for non linear analog meters
|
||||
|
||||
|
||||
|
||||
#### #12 Less reboots due to memory leakage
|
||||
|
||||
* Issue: #414 & #425 #430
|
||||
|
||||
|
||||
|
||||
#### #11 MQTT - configurable payload
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/344
|
||||
|
||||
|
||||
|
||||
#### #10 Improve and bug fix logging of images
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/307
|
||||
|
||||
|
||||
|
||||
#### #9 Basic auth for the UI
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/283
|
||||
|
||||
21
LICENSE
21
LICENSE
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 jomjol
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
105
README.md
105
README.md
@@ -6,6 +6,8 @@ This is an example of Artificial Intelligence (AI) calculations on a very cheap
|
||||
|
||||
A 3d-printable housing can be found here: https://www.thingiverse.com/thing:4573481
|
||||
|
||||
or here https://www.thingiverse.com/thing:5028229
|
||||
|
||||
respectively ESP32-Cam housing only: https://www.thingiverse.com/thing:4571627
|
||||
|
||||
<img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/watermeter_all.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/main.jpg" width="200"><img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/size.png" width="200">
|
||||
@@ -32,58 +34,92 @@ If you have any technical topics, you can file a issue in this repository.
|
||||
|
||||
In other cases you can contact the developer via email: <img src="https://raw.githubusercontent.com/jomjol/AI-on-the-edge-device/master/images/mail.jpg" height="25">
|
||||
|
||||
## Change log
|
||||
------
|
||||
## Coming next
|
||||
|
||||
* Automated update of the neural network file (tflite) to make the learing of additional pictures much easier and automated (GitHub action)
|
||||
* New "hyprid" neural network for digital numbers --> allowing the detection of intermediate states ("ring between two numbers") as a subdigit
|
||||
|
||||
|
||||
------
|
||||
|
||||
## Change log
|
||||
### Known Issues
|
||||
|
||||
* slow response of web server during picture analysis
|
||||
* spontaneous reboots (mostly due to html access during image processing) - self recovery implemented
|
||||
|
||||
------
|
||||
|
||||
**General remark:** Beside the `firmware.bin`, typically also the content of `/html` needs to be updated!
|
||||
|
||||
|
||||
|
||||
##### 8.0.2 - Multi Meter Support (2021-07-23
|
||||
|
||||
* NEW 8.0.1: saving roundes prevalue, bug fix html server
|
||||
* NEW 8.0.1: bug fix html handling of parameter `FixedExposure` and `ImageSize`
|
||||
* Dual / multi meter support (more than 1 number to be recognized)
|
||||
This is implemented with the feature "number" on the ROI definition as well as selected options
|
||||
* MQTT: standardization of the naming - including new topics (`json`, `freeMem `, `uptime`)c
|
||||
* Preparation for extended GPIO support (thanks to Zwerk2k) - not tested and fully functional yet
|
||||
* Bug fixing: html server, memory leak, MQTT connect, hostname, turn of flash LED
|
||||
|
||||
<span style="color: red;">**ATTENTION: the configuration and prevalue files are modified automatically and will not be backward compatible!**</span>
|
||||
------
|
||||
|
||||
|
||||
|
||||
##### 7.1.2 MQTT-Update - (2021-06-17)
|
||||
##### 10.1.1 - Stability Increase (2022-01-12)
|
||||
|
||||
* NEW: 7.1.2: bug fix setting hostname, Flash-LED not off during rebootNEW: 7.1.1: bug fix wlan password with "=" (again)
|
||||
* MQTT error message: changes "no error", send retain flag
|
||||
* Update wlan handling to esp-idf 4.1
|
||||
* Upgrade digital CNN to v8.7.0 (added new images)
|
||||
* Bug fix: MQTT, WLAN, LED-Controll, GPIO usage, fixed IP, calculation flow rate
|
||||
- **Bug Fix MQTT problem**
|
||||
- Issue:
|
||||
- Changing from v9.x to 10.x the MQTT-paramter "Topic" was renamed into "MainTopic" to address multiple number meters
|
||||
This renaming should have been done automatically in the background within the graphical configuration, but was not working. Instead the parameter "Topic" was deleted and "MainTopic" was set to disabled and "undefined".
|
||||
- ToDo
|
||||
- Update the `html.zip`
|
||||
- If old `config.ini` available: copy it to `/config`, open the graphical configuration and save it again.
|
||||
- If old `config.ini` not available: reset the parameter "MainTopic" within the `config.ini` manually
|
||||
- Reboot
|
||||
|
||||
##### 10.1.0 - Stability Increase (2022-01-12)
|
||||
|
||||
- Reduce ESP32 frequency to 160MHz
|
||||
- Update tflite (new source: https://github.com/espressif/tflite-micro-esp-examples)
|
||||
- Update analog neural network (ana-s3-q-20220105.tflite)
|
||||
- Update digital neural network (dig-s1-q-20220102.tflite)
|
||||
- Increased web-server buffers
|
||||
- bug fix: compiler compatibility
|
||||
|
||||
##### 10.0.2 - Stability Increase (2022-01-01)
|
||||
|
||||
- NEW v10.0.2: Corrected JSON error
|
||||
|
||||
- Updated compiler toolchain to ESP-IDF 4.3
|
||||
|
||||
- Removal of memory leak
|
||||
|
||||
- Improved error handling during startup (check PSRAM and camera with remark in logfile)
|
||||
|
||||
- MQTT: implemented raw value additionally, removal of regex contrain
|
||||
|
||||
- Normalized Parameter ``MaxRateValue`` to "change per minute"
|
||||
|
||||
- HTML: improved input handling
|
||||
|
||||
- Corrected error handling: in case of error the old value, rate, timestamp are not transmitted any more
|
||||
|
||||
|
||||
|
||||
##### 7.0.1 MQTT-Update - (2021-05-13)
|
||||
##### 9.2.0 - External Illumination (2021-12-02)
|
||||
|
||||
* NEW: 7.0.1: bug fix wlan password with "="
|
||||
- Direct JSON access: ``http://IP-ADRESS/json``
|
||||
- Error message in log file in case camera error during startup
|
||||
- Upgrade analog CNN to v9.1.0
|
||||
- Upgrade digital CNN to v13.3.0 (added new images)
|
||||
- html: support of different ports
|
||||
|
||||
* Upgrade digital CNN to v8.5.0 (added new images)
|
||||
##### 9.1.1 - External Illumination (2021-11-16)
|
||||
|
||||
- NEW 9.1.1 bug fix: LED implemenetation
|
||||
- External LEDs: change control mode (resolve bug with more than 2 LEDs)
|
||||
- Additional info into log file
|
||||
- Bug fix: decimal shift, html, log file
|
||||
|
||||
##### 9.0.0 - External Illumination (2021-10-23)
|
||||
|
||||
* Implementation of external illumination to adjust positioning, brightness and color of the illumination now individually
|
||||
* Technical details can be found in the wiki: https://github.com/jomjol/AI-on-the-edge-device/wiki/External-LED
|
||||
<img src="https://raw.githubusercontent.com/jomjol/ai-on-the-edge-device/master/images/intern_vs_external.jpg" width="500">
|
||||
* New housing published for external LEDs and small clearing: https://www.thingiverse.com/thing:5028229
|
||||
|
||||
* New MQTT topics: flow rate (units/minute), time stamp (last correct read readout)
|
||||
|
||||
* Update MQTT/Error topic to " " in case no error (instead of empty string)
|
||||
|
||||
* Portrait or landscape image orientation in rotated image (avoid cropping)
|
||||
|
||||
|
||||
|
||||
|
||||
## Additional ideas
|
||||
@@ -96,6 +132,10 @@ There are some ideas and feature request, which are not followed currently - mai
|
||||
|
||||
## History
|
||||
|
||||
##### 8.5.0 - Multi Meter Support (2021-10-07)
|
||||
|
||||
##### 7.1.2 MQTT-Update - (2021-06-17)
|
||||
|
||||
##### 6.7.2 Image Processing in Memory - (2021-05-01)
|
||||
|
||||
##### 5.0.0 Setup Modus - (2020-12-06)
|
||||
@@ -118,8 +158,3 @@ There are some ideas and feature request, which are not followed currently - mai
|
||||
|
||||
#### [Full Changelog](Changelog.md)
|
||||
|
||||
|
||||
|
||||
## Solved topics
|
||||
|
||||
* n.a.
|
||||
|
||||
3
code/.helper/copy-esp-idf.bat
Normal file
3
code/.helper/copy-esp-idf.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
copy "..\..\code\build\esp32cam-server-only.bin" "..\..\firmware\firmware.bin"
|
||||
copy "..\..\code\build\bootloader\bootloader.bin" "..\..\firmware\bootloader.bin"
|
||||
copy "..\..\code\build\partition_table\partition-table.bin" "..\..\firmware\partitions.bin"
|
||||
@@ -39,18 +39,27 @@ bool ConfigFile::GetNextParagraph(std::string& aktparamgraph, bool &disabled, bo
|
||||
bool ConfigFile::getNextLine(std::string *rt, bool &disabled, bool &eof)
|
||||
{
|
||||
eof = false;
|
||||
char zw[1024];
|
||||
char zw[1024] = "";
|
||||
if (pFile == NULL)
|
||||
{
|
||||
*rt = "";
|
||||
return false;
|
||||
}
|
||||
fgets(zw, 1024, pFile);
|
||||
printf("%s", zw);
|
||||
if ((strlen(zw) == 0) && feof(pFile))
|
||||
|
||||
if (fgets(zw, 1024, pFile))
|
||||
{
|
||||
printf("%s", zw);
|
||||
if ((strlen(zw) == 0) && feof(pFile))
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*rt = "";
|
||||
eof = true;
|
||||
eof = true;
|
||||
return false;
|
||||
}
|
||||
*rt = zw;
|
||||
|
||||
132
code/components/jomjol_controlGPIO/Color.cpp
Normal file
132
code/components/jomjol_controlGPIO/Color.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
#include "Color.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
|
||||
namespace {
|
||||
|
||||
// Int -> fixed point
|
||||
int up( int x ) { return x * 255; }
|
||||
|
||||
} // namespace
|
||||
|
||||
int iRgbSqrt( int num ) {
|
||||
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29
|
||||
assert( "sqrt input should be non-negative" && num >= 0 );
|
||||
assert( "sqrt input should no exceed 16 bits" && num <= 0xFFFF );
|
||||
int res = 0;
|
||||
int bit = 1 << 16;
|
||||
while ( bit > num )
|
||||
bit >>= 2;
|
||||
while ( bit != 0 ) {
|
||||
if ( num >= res + bit ) {
|
||||
num -= res + bit;
|
||||
res = ( res >> 1 ) + bit;
|
||||
} else
|
||||
res >>= 1;
|
||||
bit >>= 2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Rgb::Rgb( Hsv y ) {
|
||||
// https://stackoverflow.com/questions/24152553/hsv-to-rgb-and-back-without-floating-point-math-in-python
|
||||
// greyscale
|
||||
if( y.s == 0 ) {
|
||||
r = g = b = y.v;
|
||||
return;
|
||||
}
|
||||
|
||||
const int region = y.h / 43;
|
||||
const int remainder = ( y.h - ( region * 43 ) ) * 6;
|
||||
|
||||
const int p = ( y.v * ( 255 - y.s ) ) >> 8;
|
||||
const int q = ( y.v * ( 255 - ( ( y.s * remainder ) >> 8 ) ) ) >> 8;
|
||||
const int t = ( y.v * ( 255 - ( ( y.s * (255 -remainder ) ) >> 8 ) ) ) >> 8;
|
||||
|
||||
switch( region ) {
|
||||
case 0: r = y.v; g = t; b = p; break;
|
||||
case 1: r = q; g = y.v; b = p; break;
|
||||
case 2: r = p; g = y.v; b = t; break;
|
||||
case 3: r = p; g = q; b = y.v; break;
|
||||
case 4: r = t; g = p; b = y.v; break;
|
||||
case 5: r = y.v; g = p; b = q; break;
|
||||
default: __builtin_trap();
|
||||
}
|
||||
|
||||
a = y.a;
|
||||
}
|
||||
|
||||
Rgb& Rgb::operator=( Hsv hsv ) {
|
||||
Rgb r{ hsv };
|
||||
swap( r );
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rgb Rgb::operator+( Rgb in ) const {
|
||||
auto copy = *this;
|
||||
copy += in;
|
||||
return copy;
|
||||
}
|
||||
|
||||
Rgb& Rgb::operator+=( Rgb in ) {
|
||||
unsigned int red = r + in.r;
|
||||
r = ( red < 255 ) ? red : 255;
|
||||
unsigned int green = g + in.g;
|
||||
g = ( green < 255 ) ? green : 255;
|
||||
unsigned int blue = b + in.b;
|
||||
b = ( blue < 255 ) ? blue : 255;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Rgb& Rgb::blend( Rgb in ) {
|
||||
unsigned int inAlpha = in.a * ( 255 - a );
|
||||
unsigned int alpha = a + inAlpha;
|
||||
r = iRgbSqrt( ( ( r * r * a ) + ( in.r * in.r * inAlpha ) ) / alpha );
|
||||
g = iRgbSqrt( ( ( g * g * a ) + ( in.g * in.g * inAlpha ) ) / alpha );
|
||||
b = iRgbSqrt( ( ( b * b * a ) + ( in.b * in.b * inAlpha ) ) / alpha );
|
||||
a = alpha;
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR Rgb::getGrb( int idx ) {
|
||||
switch ( idx ) {
|
||||
case 0: return g;
|
||||
case 1: return r;
|
||||
case 2: return b;
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Hsv::Hsv( Rgb r ) {
|
||||
int min = std::min( r.r, std::min( r.g, r.b ) );
|
||||
int max = std::max( r.r, std::max( r.g, r.b ) );
|
||||
int chroma = max - min;
|
||||
|
||||
v = max;
|
||||
if ( chroma == 0 ) {
|
||||
h = s = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
s = up( chroma ) / max;
|
||||
int hh;
|
||||
if ( max == r.r )
|
||||
hh = ( up( int( r.g ) - int( r.b ) ) ) / chroma / 6;
|
||||
else if ( max == r.g )
|
||||
hh = 255 / 3 + ( up( int( r.b ) - int( r.r ) ) ) / chroma / 6;
|
||||
else
|
||||
hh = 2 * 255 / 3 + ( up( int( r.r ) - int( r.g ) ) ) / chroma / 6;
|
||||
|
||||
if ( hh < 0 )
|
||||
hh += 255;
|
||||
h = hh;
|
||||
|
||||
a = r.a;
|
||||
}
|
||||
|
||||
Hsv& Hsv::operator=( Rgb rgb ) {
|
||||
Hsv h{ rgb };
|
||||
swap( h );
|
||||
return *this;
|
||||
}
|
||||
69
code/components/jomjol_controlGPIO/Color.h
Normal file
69
code/components/jomjol_controlGPIO/Color.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "esp_attr.h"
|
||||
union Hsv;
|
||||
|
||||
union Rgb {
|
||||
struct __attribute__ ((packed)) {
|
||||
uint8_t r, g, b, a;
|
||||
};
|
||||
uint32_t value;
|
||||
|
||||
Rgb( uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 255 ) : r( r ), g( g ), b( b ), a( a ) {}
|
||||
Rgb( Hsv c );
|
||||
Rgb& operator=( Rgb rgb ) { swap( rgb ); return *this; }
|
||||
Rgb& operator=( Hsv hsv );
|
||||
Rgb operator+( Rgb in ) const;
|
||||
Rgb& operator+=( Rgb in );
|
||||
bool operator==( Rgb in ) const { return in.value == value; }
|
||||
Rgb& blend( Rgb in );
|
||||
void swap( Rgb& o ) { value = o.value; }
|
||||
void linearize() {
|
||||
r = channelGamma(r);
|
||||
g = channelGamma(g);
|
||||
b = channelGamma(b);
|
||||
}
|
||||
|
||||
uint8_t IRAM_ATTR getGrb( int idx );
|
||||
|
||||
void stretchChannels( uint8_t maxR, uint8_t maxG, uint8_t maxB ) {
|
||||
r = stretch( r, maxR );
|
||||
g = stretch( g, maxG );
|
||||
b = stretch( b, maxB );
|
||||
}
|
||||
|
||||
void stretchChannelsEvenly( uint8_t max ) {
|
||||
stretchChannels( max, max, max );
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t stretch( int value, uint8_t max ) {
|
||||
return ( value * max ) >> 8;
|
||||
}
|
||||
|
||||
uint8_t channelGamma( int channel ) {
|
||||
/* The optimal gamma correction is x^2.8. However, this is expensive to
|
||||
* compute. Therefore, we use x^3 for gamma correction. Also, we add a
|
||||
* bias as the WS2812 LEDs do not turn on for values less than 4. */
|
||||
if (channel == 0)
|
||||
return channel;
|
||||
channel = channel * channel * channel * 251;
|
||||
channel >>= 24;
|
||||
return static_cast< uint8_t >( 4 + channel );
|
||||
}
|
||||
};
|
||||
|
||||
union Hsv {
|
||||
struct __attribute__ ((packed)) {
|
||||
uint8_t h, s, v, a;
|
||||
};
|
||||
uint32_t value;
|
||||
|
||||
Hsv( uint8_t h, uint8_t s = 0, uint8_t v = 0, uint8_t a = 255 ) : h( h ), s( s ), v( v ), a( a ) {}
|
||||
Hsv( Rgb r );
|
||||
Hsv& operator=( Hsv h ) { swap( h ); return *this; }
|
||||
Hsv& operator=( Rgb rgb );
|
||||
bool operator==( Hsv in ) const { return in.value == value; }
|
||||
void swap( Hsv& o ) { value = o.value; }
|
||||
};
|
||||
63
code/components/jomjol_controlGPIO/SmartLeds.cpp
Normal file
63
code/components/jomjol_controlGPIO/SmartLeds.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "SmartLeds.h"
|
||||
|
||||
IsrCore SmartLed::_interruptCore = CoreCurrent;
|
||||
intr_handle_t SmartLed::_interruptHandle = NULL;
|
||||
|
||||
SmartLed*& IRAM_ATTR SmartLed::ledForChannel( int channel ) {
|
||||
static SmartLed* table[8] = { nullptr };
|
||||
assert( channel < 8 );
|
||||
return table[ channel ];
|
||||
}
|
||||
|
||||
void IRAM_ATTR SmartLed::interruptHandler(void*) {
|
||||
for (int channel = 0; channel != 8; channel++) {
|
||||
auto self = ledForChannel( channel );
|
||||
|
||||
if ( RMT.int_st.val & (1 << (24 + channel ) ) ) { // tx_thr_event
|
||||
if ( self )
|
||||
self->copyRmtHalfBlock();
|
||||
RMT.int_clr.val |= 1 << ( 24 + channel );
|
||||
} else if ( RMT.int_st.val & ( 1 << (3 * channel ) ) ) { // tx_end
|
||||
if ( self )
|
||||
xSemaphoreGiveFromISR( self->_finishedFlag, nullptr );
|
||||
RMT.int_clr.val |= 1 << ( 3 * channel );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR SmartLed::copyRmtHalfBlock() {
|
||||
int offset = detail::MAX_PULSES * _halfIdx;
|
||||
_halfIdx = !_halfIdx;
|
||||
int len = 3 - _componentPosition + 3 * ( _count - 1 );
|
||||
len = std::min( len, detail::MAX_PULSES / 8 );
|
||||
|
||||
if ( !len ) {
|
||||
for ( int i = 0; i < detail::MAX_PULSES; i++) {
|
||||
RMTMEM.chan[ _channel].data32[i + offset ].val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
for ( i = 0; i != len && _pixelPosition != _count; i++ ) {
|
||||
uint8_t val = _buffer[ _pixelPosition ].getGrb( _componentPosition );
|
||||
for ( int j = 0; j != 8; j++, val <<= 1 ) {
|
||||
int bit = val >> 7;
|
||||
int idx = i * 8 + offset + j;
|
||||
RMTMEM.chan[ _channel ].data32[ idx ].val = _bitToRmt[ bit & 0x01 ].value;
|
||||
}
|
||||
if ( _pixelPosition == _count - 1 && _componentPosition == 2 ) {
|
||||
RMTMEM.chan[ _channel ].data32[ i * 8 + offset + 7 ].duration1 =
|
||||
_timing.TRS / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
}
|
||||
|
||||
_componentPosition++;
|
||||
if ( _componentPosition == 3 ) {
|
||||
_componentPosition = 0;
|
||||
_pixelPosition++;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i *= 8; i != detail::MAX_PULSES; i++ ) {
|
||||
RMTMEM.chan[ _channel ].data32[ i + offset ].val = 0;
|
||||
}
|
||||
}
|
||||
530
code/components/jomjol_controlGPIO/SmartLeds.h
Normal file
530
code/components/jomjol_controlGPIO/SmartLeds.h
Normal file
@@ -0,0 +1,530 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
* A C++ driver for the WS2812 LEDs using the RMT peripheral on the ESP32.
|
||||
*
|
||||
* Jan "yaqwsx" Mrázek <email@honzamrazek.cz>
|
||||
*
|
||||
* Based on the work by Martin F. Falatic - https://github.com/FozzTexx/ws2812-demo
|
||||
*/
|
||||
|
||||
/*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#if defined ( ARDUINO )
|
||||
extern "C" { // ...someone forgot to put in the includes...
|
||||
#include "esp32-hal.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_ipc.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "soc/rmt_struct.h"
|
||||
#include <driver/spi_master.h>
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL( 4, 0, 0 )
|
||||
#include "soc/dport_reg.h"
|
||||
#endif
|
||||
}
|
||||
#elif defined ( ESP_PLATFORM )
|
||||
extern "C" { // ...someone forgot to put in the includes...
|
||||
#include <esp_intr_alloc.h>
|
||||
#include <esp_ipc.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <soc/dport_reg.h>
|
||||
#include <soc/gpio_sig_map.h>
|
||||
#include <soc/rmt_struct.h>
|
||||
#include <driver/spi_master.h>
|
||||
}
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "Color.h"
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct TimingParams {
|
||||
uint32_t T0H;
|
||||
uint32_t T1H;
|
||||
uint32_t T0L;
|
||||
uint32_t T1L;
|
||||
uint32_t TRS;
|
||||
};
|
||||
|
||||
union RmtPulsePair {
|
||||
struct {
|
||||
int duration0:15;
|
||||
int level0:1;
|
||||
int duration1:15;
|
||||
int level1:1;
|
||||
};
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
static const int DIVIDER = 4; // 8 still seems to work, but timings become marginal
|
||||
static const int MAX_PULSES = 32; // A channel has a 64 "pulse" buffer - we use half per pass
|
||||
static const double RMT_DURATION_NS = 12.5; // minimum time of a single RMT duration based on clock ns
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using LedType = detail::TimingParams;
|
||||
|
||||
static const LedType LED_WS2812 = { 350, 700, 800, 600, 50000 };
|
||||
static const LedType LED_WS2812B = { 400, 850, 850, 400, 50100 };
|
||||
static const LedType LED_SK6812 = { 300, 600, 900, 600, 80000 };
|
||||
static const LedType LED_WS2813 = { 350, 800, 350, 350, 300000 };
|
||||
|
||||
enum BufferType { SingleBuffer = 0, DoubleBuffer };
|
||||
|
||||
enum IsrCore { CoreFirst = 0, CoreSecond = 1, CoreCurrent = 2};
|
||||
|
||||
class SmartLed {
|
||||
public:
|
||||
// The RMT interrupt must not run on the same core as WiFi interrupts, otherwise SmartLeds
|
||||
// can't fill the RMT buffer fast enough, resulting in rendering artifacts.
|
||||
// Usually, that means you have to set isrCore == CoreSecond.
|
||||
//
|
||||
// If you use anything other than CoreCurrent, the FreeRTOS scheduler MUST be already running,
|
||||
// so you can't use it if you define SmartLed as global variable.
|
||||
SmartLed( const LedType& type, int count, int pin, int channel = 0, BufferType doubleBuffer = SingleBuffer, IsrCore isrCore = CoreCurrent)
|
||||
: _timing( type ),
|
||||
_channel( channel ),
|
||||
_count( count ),
|
||||
_firstBuffer( new Rgb[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new Rgb[ count ] : nullptr ),
|
||||
_finishedFlag( xSemaphoreCreateBinary() )
|
||||
{
|
||||
assert( channel >= 0 && channel < 8 );
|
||||
assert( ledForChannel( channel ) == nullptr );
|
||||
|
||||
xSemaphoreGive( _finishedFlag );
|
||||
|
||||
DPORT_SET_PERI_REG_MASK( DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN );
|
||||
DPORT_CLEAR_PERI_REG_MASK( DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST );
|
||||
|
||||
PIN_FUNC_SELECT( GPIO_PIN_MUX_REG[ pin ], 2 );
|
||||
gpio_set_direction( static_cast< gpio_num_t >( pin ), GPIO_MODE_OUTPUT );
|
||||
gpio_matrix_out( static_cast< gpio_num_t >( pin ), RMT_SIG_OUT0_IDX + _channel, 0, 0 );
|
||||
initChannel( _channel );
|
||||
|
||||
RMT.tx_lim_ch[ _channel ].limit = detail::MAX_PULSES;
|
||||
RMT.int_ena.val |= 1 << ( 24 + _channel );
|
||||
RMT.int_ena.val |= 1 << ( 3 * _channel );
|
||||
|
||||
_bitToRmt[ 0 ].level0 = 1;
|
||||
_bitToRmt[ 0 ].level1 = 0;
|
||||
_bitToRmt[ 0 ].duration0 = _timing.T0H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
_bitToRmt[ 0 ].duration1 = _timing.T0L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
|
||||
_bitToRmt[ 1 ].level0 = 1;
|
||||
_bitToRmt[ 1 ].level1 = 0;
|
||||
_bitToRmt[ 1 ].duration0 = _timing.T1H / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
_bitToRmt[ 1 ].duration1 = _timing.T1L / ( detail::RMT_DURATION_NS * detail::DIVIDER );
|
||||
|
||||
if ( !anyAlive() ) {
|
||||
_interruptCore = isrCore;
|
||||
if(isrCore != CoreCurrent) {
|
||||
ESP_ERROR_CHECK(esp_ipc_call_blocking(isrCore, registerInterrupt, NULL));
|
||||
} else {
|
||||
registerInterrupt(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
ledForChannel( channel ) = this;
|
||||
}
|
||||
|
||||
~SmartLed() {
|
||||
ledForChannel( _channel ) = nullptr;
|
||||
if ( !anyAlive() ) {
|
||||
if(_interruptCore != CoreCurrent) {
|
||||
ESP_ERROR_CHECK(esp_ipc_call_blocking(_interruptCore, unregisterInterrupt, NULL));
|
||||
} else {
|
||||
unregisterInterrupt(NULL);
|
||||
}
|
||||
}
|
||||
vSemaphoreDelete( _finishedFlag );
|
||||
}
|
||||
|
||||
Rgb& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
const Rgb& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
startTransmission();
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
bool wait( TickType_t timeout = portMAX_DELAY ) {
|
||||
if( xSemaphoreTake( _finishedFlag, timeout ) == pdTRUE ) {
|
||||
xSemaphoreGive( _finishedFlag );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int size() const {
|
||||
return _count;
|
||||
}
|
||||
|
||||
Rgb *begin() { return _firstBuffer.get(); }
|
||||
const Rgb *begin() const { return _firstBuffer.get(); }
|
||||
const Rgb *cbegin() const { return _firstBuffer.get(); }
|
||||
|
||||
Rgb *end() { return _firstBuffer.get() + _count; }
|
||||
const Rgb *end() const { return _firstBuffer.get() + _count; }
|
||||
const Rgb *cend() const { return _firstBuffer.get() + _count; }
|
||||
|
||||
private:
|
||||
static intr_handle_t _interruptHandle;
|
||||
static IsrCore _interruptCore;
|
||||
|
||||
static void initChannel( int channel ) {
|
||||
RMT.apb_conf.fifo_mask = 1; //enable memory access, instead of FIFO mode.
|
||||
RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
|
||||
RMT.conf_ch[ channel ].conf0.div_cnt = detail::DIVIDER;
|
||||
RMT.conf_ch[ channel ].conf0.mem_size = 1;
|
||||
RMT.conf_ch[ channel ].conf0.carrier_en = 0;
|
||||
RMT.conf_ch[ channel ].conf0.carrier_out_lv = 1;
|
||||
RMT.conf_ch[ channel ].conf0.mem_pd = 0;
|
||||
|
||||
RMT.conf_ch[ channel ].conf1.rx_en = 0;
|
||||
RMT.conf_ch[ channel ].conf1.mem_owner = 0;
|
||||
RMT.conf_ch[ channel ].conf1.tx_conti_mode = 0; //loop back mode.
|
||||
RMT.conf_ch[ channel ].conf1.ref_always_on = 1; // use apb clock: 80M
|
||||
RMT.conf_ch[ channel ].conf1.idle_out_en = 1;
|
||||
RMT.conf_ch[ channel ].conf1.idle_out_lv = 0;
|
||||
}
|
||||
|
||||
static void registerInterrupt(void *) {
|
||||
ESP_ERROR_CHECK(esp_intr_alloc( ETS_RMT_INTR_SOURCE, 0, interruptHandler, nullptr, &_interruptHandle));
|
||||
}
|
||||
|
||||
static void unregisterInterrupt(void*) {
|
||||
esp_intr_free( _interruptHandle );
|
||||
}
|
||||
|
||||
static SmartLed*& IRAM_ATTR ledForChannel( int channel );
|
||||
static void IRAM_ATTR interruptHandler( void* );
|
||||
|
||||
void IRAM_ATTR copyRmtHalfBlock();
|
||||
|
||||
void swapBuffers() {
|
||||
if ( _secondBuffer )
|
||||
_firstBuffer.swap( _secondBuffer );
|
||||
}
|
||||
|
||||
void startTransmission() {
|
||||
// Invalid use of the library
|
||||
if( xSemaphoreTake( _finishedFlag, 0 ) != pdTRUE )
|
||||
abort();
|
||||
|
||||
_pixelPosition = _componentPosition = _halfIdx = 0;
|
||||
copyRmtHalfBlock();
|
||||
if ( _pixelPosition < _count )
|
||||
copyRmtHalfBlock();
|
||||
|
||||
RMT.conf_ch[ _channel ].conf1.mem_rd_rst = 1;
|
||||
RMT.conf_ch[ _channel ].conf1.tx_start = 1;
|
||||
}
|
||||
|
||||
static bool anyAlive() {
|
||||
for ( int i = 0; i != 8; i++ )
|
||||
if ( ledForChannel( i ) != nullptr ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
const LedType& _timing;
|
||||
int _channel;
|
||||
detail::RmtPulsePair _bitToRmt[ 2 ];
|
||||
int _count;
|
||||
std::unique_ptr< Rgb[] > _firstBuffer;
|
||||
std::unique_ptr< Rgb[] > _secondBuffer;
|
||||
Rgb *_buffer;
|
||||
|
||||
xSemaphoreHandle _finishedFlag;
|
||||
|
||||
int _pixelPosition;
|
||||
int _componentPosition;
|
||||
int _halfIdx;
|
||||
};
|
||||
|
||||
class Apa102 {
|
||||
public:
|
||||
struct ApaRgb {
|
||||
ApaRgb( uint8_t r = 0, uint8_t g = 0, uint32_t b = 0, uint32_t v = 0xFF )
|
||||
: v( 0xE0 | v ), b( b ), g( g ), r( r )
|
||||
{}
|
||||
|
||||
ApaRgb& operator=( const Rgb& o ) {
|
||||
r = o.r;
|
||||
g = o.g;
|
||||
b = o.b;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ApaRgb& operator=( const Hsv& o ) {
|
||||
*this = Rgb{ o };
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8_t v, b, g, r;
|
||||
};
|
||||
|
||||
static const int FINAL_FRAME_SIZE = 4;
|
||||
static const int TRANS_COUNT = 2 + 8;
|
||||
|
||||
Apa102( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer )
|
||||
: _count( count ),
|
||||
_firstBuffer( new ApaRgb[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new ApaRgb[ count ] : nullptr ),
|
||||
_initFrame( 0 )
|
||||
{
|
||||
spi_bus_config_t buscfg;
|
||||
memset( &buscfg, 0, sizeof( buscfg ) );
|
||||
buscfg.mosi_io_num = datapin;
|
||||
buscfg.miso_io_num = -1;
|
||||
buscfg.sclk_io_num = clkpin;
|
||||
buscfg.quadwp_io_num = -1;
|
||||
buscfg.quadhd_io_num = -1;
|
||||
buscfg.max_transfer_sz = 65535;
|
||||
|
||||
spi_device_interface_config_t devcfg;
|
||||
memset( &devcfg, 0, sizeof( devcfg ) );
|
||||
devcfg.clock_speed_hz = 1000000;
|
||||
devcfg.mode = 0;
|
||||
devcfg.spics_io_num = -1;
|
||||
devcfg.queue_size = TRANS_COUNT;
|
||||
devcfg.pre_cb = nullptr;
|
||||
|
||||
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
std::fill_n( _finalFrame, FINAL_FRAME_SIZE, 0xFFFFFFFF );
|
||||
}
|
||||
|
||||
~Apa102() {
|
||||
// ToDo
|
||||
}
|
||||
|
||||
ApaRgb& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
const ApaRgb& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
startTransmission();
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
for ( int i = 0; i != _transCount; i++ ) {
|
||||
spi_transaction_t *t;
|
||||
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
|
||||
}
|
||||
}
|
||||
private:
|
||||
void swapBuffers() {
|
||||
if ( _secondBuffer )
|
||||
_firstBuffer.swap( _secondBuffer );
|
||||
}
|
||||
|
||||
void startTransmission() {
|
||||
for ( int i = 0; i != TRANS_COUNT; i++ ) {
|
||||
_transactions[ i ].cmd = 0;
|
||||
_transactions[ i ].addr = 0;
|
||||
_transactions[ i ].flags = 0;
|
||||
_transactions[ i ].rxlength = 0;
|
||||
_transactions[ i ].rx_buffer = nullptr;
|
||||
}
|
||||
// Init frame
|
||||
_transactions[ 0 ].length = 32;
|
||||
_transactions[ 0 ].tx_buffer = &_initFrame;
|
||||
spi_device_queue_trans( _spi, _transactions + 0, portMAX_DELAY );
|
||||
// Data
|
||||
_transactions[ 1 ].length = 32 * _count;
|
||||
_transactions[ 1 ].tx_buffer = _buffer;
|
||||
spi_device_queue_trans( _spi, _transactions + 1, portMAX_DELAY );
|
||||
_transCount = 2;
|
||||
// End frame
|
||||
for ( int i = 0; i != 1 + _count / 32 / FINAL_FRAME_SIZE; i++ ) {
|
||||
_transactions[ 2 + i ].length = 32 * FINAL_FRAME_SIZE;
|
||||
_transactions[ 2 + i ].tx_buffer = _finalFrame;
|
||||
spi_device_queue_trans( _spi, _transactions + 2 + i, portMAX_DELAY );
|
||||
_transCount++;
|
||||
}
|
||||
}
|
||||
|
||||
spi_device_handle_t _spi;
|
||||
int _count;
|
||||
std::unique_ptr< ApaRgb[] > _firstBuffer, _secondBuffer;
|
||||
ApaRgb *_buffer;
|
||||
|
||||
spi_transaction_t _transactions[ TRANS_COUNT ];
|
||||
int _transCount;
|
||||
|
||||
uint32_t _initFrame;
|
||||
uint32_t _finalFrame[ FINAL_FRAME_SIZE ];
|
||||
};
|
||||
|
||||
class LDP8806 {
|
||||
public:
|
||||
struct LDP8806_GRB {
|
||||
|
||||
LDP8806_GRB( uint8_t g_7bit = 0, uint8_t r_7bit = 0, uint32_t b_7bit = 0 )
|
||||
: g( g_7bit ), r( r_7bit ), b( b_7bit )
|
||||
{
|
||||
}
|
||||
|
||||
LDP8806_GRB& operator=( const Rgb& o ) {
|
||||
//Convert 8->7bit colour
|
||||
r = ( o.r * 127 / 256 ) | 0x80;
|
||||
g = ( o.g * 127 / 256 ) | 0x80;
|
||||
b = ( o.b * 127 / 256 ) | 0x80;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LDP8806_GRB& operator=( const Hsv& o ) {
|
||||
*this = Rgb{ o };
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint8_t g, r, b;
|
||||
};
|
||||
|
||||
static const int LED_FRAME_SIZE_BYTES = sizeof( LDP8806_GRB );
|
||||
static const int LATCH_FRAME_SIZE_BYTES = 3;
|
||||
static const int TRANS_COUNT_MAX = 20;//Arbitrary, supports up to 600 LED
|
||||
|
||||
LDP8806( int count, int clkpin, int datapin, BufferType doubleBuffer = SingleBuffer, uint32_t clock_speed_hz = 2000000 )
|
||||
: _count( count ),
|
||||
_firstBuffer( new LDP8806_GRB[ count ] ),
|
||||
_secondBuffer( doubleBuffer ? new LDP8806_GRB[ count ] : nullptr ),
|
||||
// one 'latch'/start-of-data mark frame for every 32 leds
|
||||
_latchFrames( ( count + 31 ) / 32 )
|
||||
{
|
||||
spi_bus_config_t buscfg;
|
||||
memset( &buscfg, 0, sizeof( buscfg ) );
|
||||
buscfg.mosi_io_num = datapin;
|
||||
buscfg.miso_io_num = -1;
|
||||
buscfg.sclk_io_num = clkpin;
|
||||
buscfg.quadwp_io_num = -1;
|
||||
buscfg.quadhd_io_num = -1;
|
||||
buscfg.max_transfer_sz = 65535;
|
||||
|
||||
spi_device_interface_config_t devcfg;
|
||||
memset( &devcfg, 0, sizeof( devcfg ) );
|
||||
devcfg.clock_speed_hz = clock_speed_hz;
|
||||
devcfg.mode = 0;
|
||||
devcfg.spics_io_num = -1;
|
||||
devcfg.queue_size = TRANS_COUNT_MAX;
|
||||
devcfg.pre_cb = nullptr;
|
||||
|
||||
auto ret = spi_bus_initialize( HSPI_HOST, &buscfg, 1 );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
ret = spi_bus_add_device( HSPI_HOST, &devcfg, &_spi );
|
||||
assert( ret == ESP_OK );
|
||||
|
||||
std::fill_n( _latchBuffer, LATCH_FRAME_SIZE_BYTES, 0x0 );
|
||||
}
|
||||
|
||||
~LDP8806() {
|
||||
// noop
|
||||
}
|
||||
|
||||
LDP8806_GRB& operator[]( int idx ) {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
const LDP8806_GRB& operator[]( int idx ) const {
|
||||
return _firstBuffer[ idx ];
|
||||
}
|
||||
|
||||
void show() {
|
||||
_buffer = _firstBuffer.get();
|
||||
startTransmission();
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
void wait() {
|
||||
while ( _transCount-- ) {
|
||||
spi_transaction_t *t;
|
||||
spi_device_get_trans_result( _spi, &t, portMAX_DELAY );
|
||||
}
|
||||
}
|
||||
private:
|
||||
void swapBuffers() {
|
||||
if ( _secondBuffer )
|
||||
_firstBuffer.swap( _secondBuffer );
|
||||
}
|
||||
|
||||
void startTransmission() {
|
||||
_transCount = 0;
|
||||
for ( int i = 0; i != TRANS_COUNT_MAX; i++ ) {
|
||||
_transactions[ i ].cmd = 0;
|
||||
_transactions[ i ].addr = 0;
|
||||
_transactions[ i ].flags = 0;
|
||||
_transactions[ i ].rxlength = 0;
|
||||
_transactions[ i ].rx_buffer = nullptr;
|
||||
}
|
||||
// LED Data
|
||||
_transactions[ 0 ].length = ( LED_FRAME_SIZE_BYTES * 8 ) * _count;
|
||||
_transactions[ 0 ].tx_buffer = _buffer;
|
||||
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
|
||||
_transCount++;
|
||||
|
||||
// 'latch'/start-of-data marker frames
|
||||
for ( int i = 0; i < _latchFrames; i++ ) {
|
||||
_transactions[ _transCount ].length = ( LATCH_FRAME_SIZE_BYTES * 8 );
|
||||
_transactions[ _transCount ].tx_buffer = _latchBuffer;
|
||||
spi_device_queue_trans( _spi, _transactions + _transCount, portMAX_DELAY );
|
||||
_transCount++;
|
||||
}
|
||||
}
|
||||
|
||||
spi_device_handle_t _spi;
|
||||
int _count;
|
||||
std::unique_ptr< LDP8806_GRB[] > _firstBuffer, _secondBuffer;
|
||||
LDP8806_GRB *_buffer;
|
||||
|
||||
spi_transaction_t _transactions[ TRANS_COUNT_MAX ];
|
||||
int _transCount;
|
||||
|
||||
int _latchFrames;
|
||||
uint8_t _latchBuffer[ LATCH_FRAME_SIZE_BYTES ];
|
||||
};
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "esp_system.h"
|
||||
#include "esp_event.h"
|
||||
|
||||
#include "server_tflite.h"
|
||||
|
||||
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
|
||||
#include "esp_log.h"
|
||||
//#include "errno.h"
|
||||
@@ -105,7 +107,8 @@ void GpioPin::init()
|
||||
//configure GPIO with the given settings
|
||||
gpio_config(&io_conf);
|
||||
|
||||
if (_interruptType != GPIO_INTR_DISABLE) {
|
||||
// if (_interruptType != GPIO_INTR_DISABLE) { // ohne GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X, wenn das genutzt wird, dann soll auch der Handler hier nicht initialisiert werden, da das dann über SmartLED erfolgt.
|
||||
if ((_interruptType != GPIO_INTR_DISABLE) && (_interruptType != GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)) {
|
||||
//hook isr handler for specific gpio pin
|
||||
ESP_LOGD(TAG_SERVERGPIO, "GpioPin::init add isr handler for GPIO %d\r\n", _gpio);
|
||||
gpio_isr_handler_add(_gpio, gpio_isr_handler, (void*)&_gpio);
|
||||
@@ -210,12 +213,15 @@ void GpioHandler::init()
|
||||
// printf("wait before start %ldms\r\n", (long) xDelay);
|
||||
// vTaskDelay( xDelay );
|
||||
|
||||
printf("*************** Start GPIOHandler_Init *****************\n");
|
||||
|
||||
if (gpioMap == NULL) {
|
||||
gpioMap = new std::map<gpio_num_t, GpioPin*>();
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
ESP_LOGI(TAG_SERVERGPIO, "read GPIO config and init GPIO");
|
||||
if (!readConfig()) {
|
||||
clear();
|
||||
@@ -225,6 +231,7 @@ void GpioHandler::init()
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for(std::map<gpio_num_t, GpioPin*>::iterator it = gpioMap->begin(); it != gpioMap->end(); ++it) {
|
||||
it->second->init();
|
||||
}
|
||||
@@ -291,17 +298,32 @@ bool GpioHandler::readConfig()
|
||||
std::string line = "";
|
||||
bool disabledLine = false;
|
||||
bool eof = false;
|
||||
gpio_num_t gpioExtLED = (gpio_num_t) 0;
|
||||
|
||||
// printf("readConfig - Start 1\n");
|
||||
|
||||
while ((!configFile.GetNextParagraph(line, disabledLine, eof) || (line.compare("[GPIO]") != 0)) && !disabledLine && !eof) {}
|
||||
while ((!configFile.GetNextParagraph(line, disabledLine, eof) || (line.compare("[GPIO]") != 0)) && !eof) {}
|
||||
if (eof)
|
||||
return false;
|
||||
|
||||
|
||||
// printf("readConfig - Start 2 line: %s, disabbledLine: %d\n", line.c_str(), (int) disabledLine);
|
||||
|
||||
|
||||
_isEnabled = !disabledLine;
|
||||
|
||||
if (!_isEnabled)
|
||||
return false;
|
||||
|
||||
std::string mainTopicMQTT = "";
|
||||
// printf("readConfig - Start 3\n");
|
||||
|
||||
// std::string mainTopicMQTT = "";
|
||||
std::string mainTopicMQTT = GetMQTTMainTopic();
|
||||
if (mainTopicMQTT.length() > 0)
|
||||
{
|
||||
mainTopicMQTT = mainTopicMQTT + "/GPIO";
|
||||
ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found\r\n");
|
||||
}
|
||||
|
||||
bool registerISR = false;
|
||||
while (configFile.getNextLine(&line, disabledLine, eof) && !configFile.isNewParagraph(line))
|
||||
{
|
||||
@@ -313,8 +335,8 @@ bool GpioHandler::readConfig()
|
||||
// std::string gpioStr = pieces_match[1];
|
||||
ESP_LOGD(TAG_SERVERGPIO, "conf param %s\r\n", toUpper(zerlegt[0]).c_str());
|
||||
if (toUpper(zerlegt[0]) == "MAINTOPICMQTT") {
|
||||
ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found\r\n");
|
||||
mainTopicMQTT = zerlegt[1];
|
||||
// ESP_LOGD(TAG_SERVERGPIO, "MAINTOPICMQTT found\r\n");
|
||||
// mainTopicMQTT = zerlegt[1];
|
||||
} else if ((zerlegt[0].rfind("IO", 0) == 0) && (zerlegt.size() >= 6))
|
||||
{
|
||||
ESP_LOGI(TAG_SERVERGPIO,"Enable GP%s in %s mode", zerlegt[0].c_str(), zerlegt[1].c_str());
|
||||
@@ -335,10 +357,40 @@ bool GpioHandler::readConfig()
|
||||
GpioPin* gpioPin = new GpioPin(gpioNr, gpioName, pinMode, intType,dutyResolution, mqttTopic, httpEnabled);
|
||||
(*gpioMap)[gpioNr] = gpioPin;
|
||||
|
||||
if (pinMode == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)
|
||||
{
|
||||
printf("Set WS2812 to GPIO %d\n", gpioNr);
|
||||
gpioExtLED = gpioNr;
|
||||
}
|
||||
|
||||
if (intType != GPIO_INTR_DISABLE) {
|
||||
registerISR = true;
|
||||
}
|
||||
}
|
||||
if (toUpper(zerlegt[0]) == "LEDNUMBERS")
|
||||
{
|
||||
LEDNumbers = stoi(zerlegt[1]);
|
||||
}
|
||||
if (toUpper(zerlegt[0]) == "LEDCOLOR")
|
||||
{
|
||||
uint8_t _r, _g, _b;
|
||||
_r = stoi(zerlegt[1]);
|
||||
_g = stoi(zerlegt[2]);
|
||||
_b = stoi(zerlegt[3]);
|
||||
|
||||
LEDColor = Rgb{_r, _g, _b};
|
||||
}
|
||||
if (toUpper(zerlegt[0]) == "LEDTYPE")
|
||||
{
|
||||
if (zerlegt[1] == "WS2812")
|
||||
LEDType = LED_WS2812;
|
||||
if (zerlegt[1] == "WS2812B")
|
||||
LEDType = LED_WS2812B;
|
||||
if (zerlegt[1] == "SK6812")
|
||||
LEDType = LED_SK6812;
|
||||
if (zerlegt[1] == "WS2813")
|
||||
LEDType = LED_WS2813;
|
||||
}
|
||||
}
|
||||
|
||||
if (registerISR) {
|
||||
@@ -346,6 +398,23 @@ bool GpioHandler::readConfig()
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM);
|
||||
}
|
||||
|
||||
if (gpioExtLED > 0)
|
||||
{
|
||||
// LogFile.WriteToFile("Startsequence 06"); // Nremove
|
||||
// vTaskDelay( xDelay );
|
||||
// xDelay = 5000 / portTICK_PERIOD_MS;
|
||||
// printf("main: sleep for : %ldms\n", (long) xDelay);
|
||||
|
||||
// SmartLed leds( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer );
|
||||
|
||||
|
||||
// leds[ 0 ] = Rgb{ 255, 0, 0 };
|
||||
// leds[ 1 ] = Rgb{ 255, 255, 255 };
|
||||
// leds.show();
|
||||
// SmartLed leds = new SmartLed(LEDType, LEDNumbers, gpioExtLED, 0, DoubleBuffer);
|
||||
// _SmartLED = new SmartLed( LED_WS2812, 2, GPIO_NUM_12, 0, DoubleBuffer );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -487,7 +556,47 @@ void GpioHandler::flashLightEnable(bool value)
|
||||
} else {
|
||||
ESP_LOGE(TAG_SERVERGPIO, "Can't set flash light pin GPIO %d. Error: %s\r\n", (int)it->first, resp_str.c_str());
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (it->second->getMode() == GPIO_PIN_MODE_EXTERNAL_FLASH_WS281X)
|
||||
{
|
||||
#ifdef __LEDGLOBAL
|
||||
if (leds_global == NULL) {
|
||||
ESP_LOGI(TAG_SERVERGPIO, "init SmartLed: LEDNumber=%d, GPIO=%d", LEDNumbers, (int)it->second->getGPIO());
|
||||
leds_global = new SmartLed( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer );
|
||||
} else {
|
||||
// wait until we can update: https://github.com/RoboticsBrno/SmartLeds/issues/10#issuecomment-386921623
|
||||
leds_global->wait();
|
||||
}
|
||||
#else
|
||||
SmartLed leds( LEDType, LEDNumbers, it->second->getGPIO(), 0, DoubleBuffer );
|
||||
#endif
|
||||
|
||||
if (value)
|
||||
{
|
||||
for (int i = 0; i < LEDNumbers; ++i)
|
||||
#ifdef __LEDGLOBAL
|
||||
(*leds_global)[i] = LEDColor;
|
||||
#else
|
||||
leds[i] = LEDColor;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < LEDNumbers; ++i)
|
||||
#ifdef __LEDGLOBAL
|
||||
(*leds_global)[i] = Rgb{0, 0, 0};
|
||||
#else
|
||||
leds[i] = Rgb{0, 0, 0};
|
||||
#endif
|
||||
}
|
||||
#ifdef __LEDGLOBAL
|
||||
leds_global->show();
|
||||
#else
|
||||
leds.show();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,13 @@
|
||||
#include <map>
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#include "SmartLeds.h"
|
||||
|
||||
//#include "ClassControllCamera.h"
|
||||
|
||||
// wenn __LEDGLOBAL definiert ist, wird eine globale Variable für die LED-Ansteuerung verwendet, ansonsten lokal und jedesmal neu
|
||||
#define __LEDGLOBAL
|
||||
|
||||
typedef enum {
|
||||
GPIO_PIN_MODE_DISABLED = 0x0,
|
||||
GPIO_PIN_MODE_INPUT = 0x1,
|
||||
@@ -45,6 +50,7 @@ public:
|
||||
void gpioInterrupt(int value);
|
||||
gpio_int_type_t getInterruptType() { return _interruptType; }
|
||||
gpio_pin_mode_t getMode() { return _mode; }
|
||||
gpio_num_t getGPIO(){return _gpio;};
|
||||
|
||||
private:
|
||||
gpio_num_t _gpio;
|
||||
@@ -80,6 +86,13 @@ private:
|
||||
TaskHandle_t xHandleTaskGpio = NULL;
|
||||
bool _isEnabled = false;
|
||||
|
||||
int LEDNumbers = 2;
|
||||
Rgb LEDColor = Rgb{ 255, 255, 255 };
|
||||
LedType LEDType = LED_WS2812;
|
||||
#ifdef __LEDGLOBAL
|
||||
SmartLed *leds_global = NULL;
|
||||
#endif
|
||||
|
||||
bool readConfig();
|
||||
void clear();
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ static camera_config_t camera_config = {
|
||||
|
||||
//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
|
||||
// .xclk_freq_hz = 20000000, // Orginalwert
|
||||
.xclk_freq_hz = 5000000, // Test, um die Bildfehler los zu werden !!!!
|
||||
.xclk_freq_hz = 5000000, // Test, um die Bildfehler los zu werden !!!!
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
|
||||
@@ -86,6 +86,8 @@ static camera_config_t camera_config = {
|
||||
|
||||
.jpeg_quality = 5, //0-63 lower number means higher quality
|
||||
.fb_count = 1 //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
// .grab_mode = CAMERA_GRAB_WHEN_EMPTY,
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -279,13 +281,23 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
|
||||
ESP_LOGE(TAGCAMERACLASS, "CaptureToBasisImage: Camera Capture Failed");
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
doReboot();
|
||||
|
||||
LogFile.SwitchOnOff(true);
|
||||
LogFile.WriteToFile("Camera is not working anymore - most propably hardware problem (instablility, ...). "
|
||||
"System will reboot.");
|
||||
doReboot();
|
||||
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int _size = fb->len;
|
||||
zwischenspeicher = (uint8_t*) malloc(_size);
|
||||
if (!zwischenspeicher)
|
||||
{
|
||||
ESP_LOGE(TAGCAMERACLASS, "Nicht ausreichend Speicherplatz für Bild in Funktion CaptureToBasisImage()");
|
||||
LogFile.SwitchOnOff(true);
|
||||
LogFile.WriteToFile("Nicht ausreichend Speicherplatz für Bild in Funktion CaptureToBasisImage()");
|
||||
}
|
||||
for (int i = 0; i < _size; ++i)
|
||||
*(zwischenspeicher + i) = *(fb->buf + i);
|
||||
esp_camera_fb_return(fb);
|
||||
@@ -499,6 +511,7 @@ void CCamera::LightOnOff(bool status)
|
||||
{
|
||||
GpioHandler* gpioHandler = gpio_handler_get();
|
||||
if ((gpioHandler != NULL) && (gpioHandler->isEnabled())) {
|
||||
printf("Use gpioHandler flashLigh\n");
|
||||
gpioHandler->flashLightEnable(status);
|
||||
} else {
|
||||
// Init the GPIO
|
||||
|
||||
@@ -11,11 +11,48 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define OV9650_PID (0x96)
|
||||
#define OV7725_PID (0x77)
|
||||
#define OV2640_PID (0x26)
|
||||
#define OV3660_PID (0x36)
|
||||
#define OV5640_PID (0x56)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
OV9650_PID = 0x96,
|
||||
OV7725_PID = 0x77,
|
||||
OV2640_PID = 0x26,
|
||||
OV3660_PID = 0x3660,
|
||||
OV5640_PID = 0x5640,
|
||||
OV7670_PID = 0x76,
|
||||
NT99141_PID = 0x1410,
|
||||
GC2145_PID = 0x2145,
|
||||
GC032A_PID = 0x232a,
|
||||
GC0308_PID = 0x9b,
|
||||
} camera_pid_t;
|
||||
|
||||
typedef enum {
|
||||
CAMERA_OV7725,
|
||||
CAMERA_OV2640,
|
||||
CAMERA_OV3660,
|
||||
CAMERA_OV5640,
|
||||
CAMERA_OV7670,
|
||||
CAMERA_NT99141,
|
||||
CAMERA_GC2145,
|
||||
CAMERA_GC032A,
|
||||
CAMERA_GC0308,
|
||||
CAMERA_MODEL_MAX,
|
||||
CAMERA_NONE,
|
||||
} camera_model_t;
|
||||
|
||||
typedef enum {
|
||||
OV2640_SCCB_ADDR = 0x30,// 0x60 >> 1
|
||||
OV5640_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
OV3660_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
OV7725_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
OV7670_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
NT99141_SCCB_ADDR = 0x2A,// 0x54 >> 1
|
||||
GC2145_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
} camera_sccb_addr_t;
|
||||
|
||||
typedef enum {
|
||||
PIXFORMAT_RGB565, // 2BPP/RGB565
|
||||
@@ -56,6 +93,15 @@ typedef enum {
|
||||
FRAMESIZE_INVALID
|
||||
} framesize_t;
|
||||
|
||||
typedef struct {
|
||||
const camera_model_t model;
|
||||
const char *name;
|
||||
const camera_sccb_addr_t sccb_addr;
|
||||
const camera_pid_t pid;
|
||||
const framesize_t max_size;
|
||||
const bool support_jpeg;
|
||||
} camera_sensor_info_t;
|
||||
|
||||
typedef enum {
|
||||
ASPECT_RATIO_4X3,
|
||||
ASPECT_RATIO_3X2,
|
||||
@@ -99,11 +145,13 @@ typedef struct {
|
||||
|
||||
// Resolution table (in sensor.c)
|
||||
extern const resolution_info_t resolution[];
|
||||
// camera sensor table (in sensor.c)
|
||||
extern const camera_sensor_info_t camera_sensor[];
|
||||
|
||||
typedef struct {
|
||||
uint8_t MIDH;
|
||||
uint8_t MIDL;
|
||||
uint8_t PID;
|
||||
uint16_t PID;
|
||||
uint8_t VER;
|
||||
} sensor_id_t;
|
||||
|
||||
@@ -188,4 +236,10 @@ typedef struct _sensor {
|
||||
int (*set_xclk) (sensor_t *sensor, int timer, int xclk);
|
||||
} sensor_t;
|
||||
|
||||
camera_sensor_info_t *esp_camera_sensor_get_info(sensor_id_t *id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SENSOR_H__ */
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#define SCRATCH_BUFSIZE2 8192
|
||||
char scratch2[SCRATCH_BUFSIZE2];
|
||||
// #define SCRATCH_BUFSIZE2 8192
|
||||
// char scratch2[SCRATCH_BUFSIZE2];
|
||||
|
||||
//#define DEBUG_DETAIL_ON
|
||||
static const char *TAGPARTCAMERA = "server_camera";
|
||||
|
||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "." "../../include"
|
||||
REQUIRES tfmicro esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
|
||||
REQUIRES tflite-lib esp_http_server app_update esp_http_client nvs_flash jomjol_tfliteclass jomjol_flowcontroll spiffs jomjol_helper jomjol_controlGPIO)
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,14 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
@@ -58,6 +65,52 @@ struct file_server_data {
|
||||
|
||||
static const char *TAG_FILESERVER = "file_server";
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static esp_err_t get_tflite_file_handler(httpd_req_t *req){
|
||||
DIR *verzeichnis;
|
||||
struct dirent *files;
|
||||
|
||||
std::string _filename, _fileext, _result = "";
|
||||
std::string _delimiter = ".";
|
||||
size_t pos = 0;
|
||||
|
||||
verzeichnis=opendir("/sdcard/config");
|
||||
|
||||
printf("Suche TFLITE in /sdcard/config\n");
|
||||
|
||||
while((files = readdir(verzeichnis)))
|
||||
{
|
||||
_filename = files->d_name;
|
||||
_fileext = _filename;
|
||||
printf("File: %s\t", _filename.c_str());
|
||||
|
||||
while ((pos = _fileext.find(_delimiter))) {
|
||||
_fileext.erase(0, pos + _delimiter.length());
|
||||
}
|
||||
|
||||
printf(" Extension: %s\n", _fileext.c_str());
|
||||
|
||||
if ((_fileext == "tfl") || (_fileext == "tflite"))
|
||||
{
|
||||
_result = _result + _filename + "\t";
|
||||
}
|
||||
}
|
||||
closedir(verzeichnis);
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_set_type(req, "text/plain");
|
||||
httpd_resp_sendstr_chunk(req, _result.c_str());
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
/* Handler to redirect incoming GET request for /index.html to /
|
||||
* This can be overridden by uploading file with same name */
|
||||
// static esp_err_t index_html_get_handler(httpd_req_t *req)
|
||||
@@ -486,10 +539,10 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
|
||||
printf("Directory: %s, start_fn: %d, found: %d\n", directory.c_str(), start_fn, found);
|
||||
directory = directory.substr(start_fn, found - start_fn + 1);
|
||||
printf("Directory danach: %s\n", directory.c_str());
|
||||
printf("Directory danach 1: %s\n", directory.c_str());
|
||||
|
||||
directory = "/fileserver" + directory;
|
||||
printf("Directory danach: %s\n", directory.c_str());
|
||||
printf("Directory danach 2: %s\n", directory.c_str());
|
||||
|
||||
/* Redirect onto root to see the updated file list */
|
||||
httpd_resp_set_status(req, "303 See Other");
|
||||
@@ -500,11 +553,13 @@ static esp_err_t upload_post_handler(httpd_req_t *req)
|
||||
httpd_resp_set_hdr(req, "Location", directory.c_str());
|
||||
httpd_resp_sendstr(req, "File uploaded successfully");
|
||||
|
||||
/*
|
||||
if (strcmp(filepath, CONFIG_FILE) == 0) {
|
||||
printf("New config foung. Reload handler.");
|
||||
printf("New config found. Reload handler.");
|
||||
gpio_handler_deinit();
|
||||
MQTTdestroy();
|
||||
}
|
||||
*/
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -606,10 +661,10 @@ static esp_err_t delete_post_handler(httpd_req_t *req)
|
||||
int start_fn = strlen(((struct file_server_data *)req->user_ctx)->base_path);
|
||||
printf("Directory: %s, start_fn: %d, found: %d\n", directory.c_str(), start_fn, found);
|
||||
directory = directory.substr(start_fn, found - start_fn + 1);
|
||||
printf("Directory danach: %s\n", directory.c_str());
|
||||
printf("Directory danach 3: %s\n", directory.c_str());
|
||||
|
||||
directory = "/fileserver" + directory;
|
||||
printf("Directory danach: %s\n", directory.c_str());
|
||||
printf("Directory danach 4: %s\n", directory.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -795,4 +850,15 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path)
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_delete);
|
||||
|
||||
|
||||
/* URI handler for getting tflite files from server */
|
||||
/*
|
||||
httpd_uri_t file_tflite = {
|
||||
.uri = "/tflite", // Match all URIs of type /delete/path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = get_tflite_file_handler,
|
||||
.user_ctx = server_data // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &file_tflite);
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -5,7 +5,14 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
@@ -111,6 +118,8 @@ esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename)
|
||||
return httpd_resp_set_type(req, "image/jpeg");
|
||||
} else if (IS_FILE_EXT(filename, ".ico")) {
|
||||
return httpd_resp_set_type(req, "image/x-icon");
|
||||
} else if (IS_FILE_EXT(filename, ".js")) {
|
||||
return httpd_resp_set_type(req, "text/javascript");
|
||||
}
|
||||
/* This is a limited set only */
|
||||
/* For any other type always set as plain text */
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "server_file.h"
|
||||
#include "server_GPIO.h"
|
||||
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
#include "Helper.h"
|
||||
|
||||
@@ -130,11 +130,9 @@ bool ClassFlow::getNextLine(FILE* pfile, string *rt)
|
||||
*rt = trim(*rt);
|
||||
while ((zw[0] == ';' || zw[0] == '#' || (rt->size() == 0)) && !(zw[1] == '[')) // Kommentarzeilen (; oder #) und Leerzeilen überspringen, es sei denn es ist ein neuer auskommentierter Paragraph
|
||||
{
|
||||
*rt = "";
|
||||
if (!fgets(zw, 1024, pfile))
|
||||
{
|
||||
*rt = "";
|
||||
return false;
|
||||
}
|
||||
printf("%s", zw);
|
||||
*rt = zw;
|
||||
*rt = trim(*rt);
|
||||
|
||||
@@ -1,487 +0,0 @@
|
||||
#include "ClassFlowAnalog.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <iomanip>
|
||||
#include <sys/types.h>
|
||||
#include <sstream> // std::stringstream
|
||||
|
||||
|
||||
// #define OHNETFLITE
|
||||
|
||||
#ifndef OHNETFLITE
|
||||
#include "CTfLiteClass.h"
|
||||
#endif
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
static const char* TAG = "flow_analog";
|
||||
|
||||
bool debugdetailanalog = false;
|
||||
|
||||
void ClassFlowAnalog::SetInitialParameter(void)
|
||||
{
|
||||
string cnnmodelfile = "";
|
||||
modelxsize = 1;
|
||||
modelysize = 1;
|
||||
ListFlowControll = NULL;
|
||||
previousElement = NULL;
|
||||
SaveAllFiles = false;
|
||||
disabled = false;
|
||||
extendedResolution = false;
|
||||
}
|
||||
|
||||
ClassFlowAnalog::ClassFlowAnalog(std::vector<ClassFlow*>* lfc) : ClassFlowImage(lfc, TAG)
|
||||
{
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
||||
{
|
||||
flowpostalignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowAnalog::AnzahlROIs(int _analog = 0)
|
||||
{
|
||||
int zw = ANALOG[_analog]->ROI.size();
|
||||
if (extendedResolution)
|
||||
zw++;
|
||||
|
||||
return zw;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowAnalog::getReadout(int _analog = 0)
|
||||
{
|
||||
string result = "";
|
||||
if (ANALOG[_analog]->ROI.size() == 0)
|
||||
return result;
|
||||
|
||||
|
||||
float zahl = ANALOG[_analog]->ROI[ANALOG[_analog]->ROI.size() - 1]->result;
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
|
||||
int prev = -1;
|
||||
|
||||
prev = ZeigerEval(ANALOG[_analog]->ROI[ANALOG[_analog]->ROI.size() - 1]->result, prev);
|
||||
result = std::to_string(prev);
|
||||
|
||||
if (extendedResolution)
|
||||
result = result + std::to_string(ergebnis_nachkomma);
|
||||
|
||||
for (int i = ANALOG[_analog]->ROI.size() - 2; i >= 0; --i)
|
||||
{
|
||||
prev = ZeigerEval(ANALOG[_analog]->ROI[i]->result, prev);
|
||||
result = std::to_string(prev) + result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ClassFlowAnalog::ZeigerEval(float zahl, int ziffer_vorgaenger)
|
||||
{
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
|
||||
int ergebnis, ergebnis_rating;
|
||||
|
||||
if (ziffer_vorgaenger == -1)
|
||||
return ergebnis_vorkomma % 10;
|
||||
|
||||
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
|
||||
if (ergebnis_nachkomma >= 5)
|
||||
ergebnis_rating-=5;
|
||||
else
|
||||
ergebnis_rating+=5;
|
||||
ergebnis = (int) round(zahl);
|
||||
if (ergebnis_rating < 0)
|
||||
ergebnis-=1;
|
||||
if (ergebnis == -1)
|
||||
ergebnis+=10;
|
||||
|
||||
ergebnis = ergebnis % 10;
|
||||
return ergebnis;
|
||||
}
|
||||
|
||||
bool ClassFlowAnalog::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> zerlegt;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
|
||||
if ((aktparamgraph.compare("[Analog]") != 0) && (aktparamgraph.compare(";[Analog]") != 0)) // Paragraph passt nich zu MakeImage
|
||||
return false;
|
||||
|
||||
if (aktparamgraph[0] == ';')
|
||||
{
|
||||
disabled = true;
|
||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
printf("[Analog] is disabled !!!\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->LogImageLocation = "/sdcard" + zerlegt[1];
|
||||
this->isLogImage = true;
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "LOGFILERETENTIONINDAYS") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->logfileRetentionInDays = std::stoi(zerlegt[1]);
|
||||
}
|
||||
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->cnnmodelfile = zerlegt[1];
|
||||
}
|
||||
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
||||
{
|
||||
this->modelxsize = std::stoi(zerlegt[1]);
|
||||
this->modelysize = std::stoi(zerlegt[2]);
|
||||
}
|
||||
if (zerlegt.size() >= 5)
|
||||
{
|
||||
analog* _analog = GetANALOG(zerlegt[0], true);
|
||||
roianalog* neuroi = _analog->ROI[_analog->ROI.size()-1];
|
||||
neuroi->posx = std::stoi(zerlegt[1]);
|
||||
neuroi->posy = std::stoi(zerlegt[2]);
|
||||
neuroi->deltax = std::stoi(zerlegt[3]);
|
||||
neuroi->deltay = std::stoi(zerlegt[4]);
|
||||
neuroi->result = -1;
|
||||
neuroi->image = NULL;
|
||||
neuroi->image_org = NULL;
|
||||
// ROI.push_back(neuroi);
|
||||
}
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
SaveAllFiles = true;
|
||||
}
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "EXTENDEDRESOLUTION") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
extendedResolution = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
|
||||
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
ANALOG[_ana]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
|
||||
ANALOG[_ana]->ROI[i]->image_org = new CImageBasis(ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
analog* ClassFlowAnalog::FindANALOG(string _name_number)
|
||||
{
|
||||
|
||||
for (int i = 0; i < ANALOG.size(); ++i)
|
||||
{
|
||||
if (ANALOG[i]->name == _name_number)
|
||||
return ANALOG[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
analog* ClassFlowAnalog::GetANALOG(string _name, bool _create = true)
|
||||
{
|
||||
string _analog, _roi;
|
||||
int _pospunkt = _name.find_first_of(".");
|
||||
// printf("Name: %s, Pospunkt: %d\n", _name.c_str(), _pospunkt);
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_analog = _name.substr(0, _pospunkt);
|
||||
_roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
_analog = "default";
|
||||
_roi = _name;
|
||||
}
|
||||
|
||||
analog *_ret = NULL;
|
||||
|
||||
for (int i = 0; i < ANALOG.size(); ++i)
|
||||
{
|
||||
if (ANALOG[i]->name == _analog)
|
||||
_ret = ANALOG[i];
|
||||
}
|
||||
|
||||
if (!_create) // nicht gefunden und soll auch nicht erzeugt werden
|
||||
return _ret;
|
||||
|
||||
|
||||
if (_ret == NULL)
|
||||
{
|
||||
_ret = new analog;
|
||||
_ret->name = _analog;
|
||||
ANALOG.push_back(_ret);
|
||||
}
|
||||
|
||||
roianalog* neuroi = new roianalog;
|
||||
neuroi->name = _roi;
|
||||
_ret->ROI.push_back(neuroi);
|
||||
|
||||
printf("GetANALOG - ANALOG %s - roi %s\n", _analog.c_str(), _roi.c_str());
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
string ClassFlowAnalog::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result, zw;
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
|
||||
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
|
||||
result = result + "Analog Pointers: <p> ";
|
||||
|
||||
htmlinfo = GetHTMLInfo();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
|
||||
zw = stream.str();
|
||||
|
||||
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ClassFlowAnalog::doFlow(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
if (!doAlignAndCut(time)){
|
||||
return false;
|
||||
};
|
||||
|
||||
if (debugdetailanalog) LogFile.WriteToFile("ClassFlowAnalog::doFlow nach Alignment");
|
||||
|
||||
doNeuralNetwork(time);
|
||||
|
||||
RemoveOldLogs();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassFlowAnalog::doAlignAndCut(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
|
||||
|
||||
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
|
||||
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
printf("Analog %d - Align&Cut\n", i);
|
||||
|
||||
caic->CutAndSave(ANALOG[_ana]->ROI[i]->posx, ANALOG[_ana]->ROI[i]->posy, ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, ANALOG[_ana]->ROI[i]->image_org);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (ANALOG[_ana]->name == "default")
|
||||
ANALOG[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".jpg"));
|
||||
else
|
||||
ANALOG[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".jpg"));
|
||||
}
|
||||
|
||||
ANALOG[_ana]->ROI[i]->image_org->Resize(modelxsize, modelysize, ANALOG[_ana]->ROI[i]->image);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (ANALOG[_ana]->name == "default")
|
||||
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
|
||||
else
|
||||
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassFlowAnalog::DrawROI(CImageBasis *_zw)
|
||||
{
|
||||
int r = 0;
|
||||
int g = 255;
|
||||
int b = 0;
|
||||
|
||||
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
|
||||
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
_zw->drawRect(ANALOG[_ana]->ROI[i]->posx, ANALOG[_ana]->ROI[i]->posy, ANALOG[_ana]->ROI[i]->deltax, ANALOG[_ana]->ROI[i]->deltay, r, g, b, 1);
|
||||
_zw->drawCircle((int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), (int) (ANALOG[_ana]->ROI[i]->deltax/2), r, g, b, 2);
|
||||
_zw->drawLine((int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) ANALOG[_ana]->ROI[i]->posy, (int) (ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax/2), (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay), r, g, b, 2);
|
||||
_zw->drawLine((int) ANALOG[_ana]->ROI[i]->posx, (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), (int) ANALOG[_ana]->ROI[i]->posx + ANALOG[_ana]->ROI[i]->deltax, (int) (ANALOG[_ana]->ROI[i]->posy + ANALOG[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassFlowAnalog::doNeuralNetwork(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
string logPath = CreateLogFolder(time);
|
||||
|
||||
string input = "/sdcard/img_tmp/alg.jpg";
|
||||
string ioresize = "/sdcard/img_tmp/resize.bmp";
|
||||
string output;
|
||||
input = FormatFileName(input);
|
||||
|
||||
#ifndef OHNETFLITE
|
||||
CTfLiteClass *tflite = new CTfLiteClass;
|
||||
string zwcnn = "/sdcard" + cnnmodelfile;
|
||||
zwcnn = FormatFileName(zwcnn);
|
||||
printf(zwcnn.c_str());printf("\n");
|
||||
if (!tflite->LoadModel(zwcnn)) {
|
||||
printf("Can't read model file /sdcard%s\n", cnnmodelfile.c_str());
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
tflite->MakeAllocate();
|
||||
#endif
|
||||
|
||||
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
|
||||
{
|
||||
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
printf("Analog %d - TfLite\n", i);
|
||||
|
||||
float f1, f2;
|
||||
f1 = 0; f2 = 0;
|
||||
|
||||
#ifndef OHNETFLITE
|
||||
tflite->LoadInputImageBasis(ANALOG[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
if (debugdetailanalog) LogFile.WriteToFile("Nach Invoke");
|
||||
|
||||
|
||||
f1 = tflite->GetOutputValue(0);
|
||||
f2 = tflite->GetOutputValue(1);
|
||||
#endif
|
||||
|
||||
float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
|
||||
// printf("Result sin, cos, ziffer: %f, %f, %f\n", f1, f2, result);
|
||||
ANALOG[_ana]->ROI[i]->result = result * 10;
|
||||
|
||||
printf("Result Analog%i: %f\n", i, ANALOG[_ana]->ROI[i]->result);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
LogImage(logPath, ANALOG[_ana]->ROI[i]->name, &ANALOG[_ana]->ROI[i]->result, NULL, time, ANALOG[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef OHNETFLITE
|
||||
delete tflite;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowAnalog::GetHTMLInfo()
|
||||
{
|
||||
std::vector<HTMLInfo*> result;
|
||||
|
||||
for (int _ana = 0; _ana < ANALOG.size(); ++_ana)
|
||||
for (int i = 0; i < ANALOG[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
if (ANALOG[_ana]->name == "default")
|
||||
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
|
||||
else
|
||||
ANALOG[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp"));
|
||||
|
||||
|
||||
HTMLInfo *zw = new HTMLInfo;
|
||||
if (ANALOG[_ana]->name == "default")
|
||||
{
|
||||
zw->filename = ANALOG[_ana]->ROI[i]->name + ".bmp";
|
||||
zw->filename_org = ANALOG[_ana]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
else
|
||||
{
|
||||
zw->filename = ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".bmp";
|
||||
zw->filename_org = ANALOG[_ana]->name + "_" + ANALOG[_ana]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
|
||||
zw->val = ANALOG[_ana]->ROI[i]->result;
|
||||
zw->image = ANALOG[_ana]->ROI[i]->image;
|
||||
zw->image_org = ANALOG[_ana]->ROI[i]->image_org;
|
||||
|
||||
result.push_back(zw);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int ClassFlowAnalog::getAnzahlANALOG()
|
||||
{
|
||||
return ANALOG.size();
|
||||
}
|
||||
|
||||
string ClassFlowAnalog::getNameANALOG(int _analog)
|
||||
{
|
||||
if (_analog < ANALOG.size())
|
||||
return ANALOG[_analog]->name;
|
||||
|
||||
return "ANALOG DOES NOT EXIST";
|
||||
}
|
||||
|
||||
analog* ClassFlowAnalog::GetANALOG(int _analog)
|
||||
{
|
||||
if (_analog < ANALOG.size())
|
||||
return ANALOG[_analog];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClassFlowAnalog::UpdateNameNumbers(std::vector<std::string> *_name_numbers)
|
||||
{
|
||||
for (int _dig = 0; _dig < ANALOG.size(); _dig++)
|
||||
{
|
||||
std::string _name = ANALOG[_dig]->name;
|
||||
bool found = false;
|
||||
for (int i = 0; i < (*_name_numbers).size(); ++i)
|
||||
{
|
||||
if ((*_name_numbers)[i] == _name)
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
(*_name_numbers).push_back(_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
#pragma once
|
||||
#include "ClassFlowImage.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
// #include "CTfLiteClass.h"
|
||||
|
||||
struct roianalog {
|
||||
int posx, posy, deltax, deltay;
|
||||
float result;
|
||||
CImageBasis *image, *image_org;
|
||||
string name;
|
||||
};
|
||||
|
||||
struct analog {
|
||||
string name;
|
||||
std::vector<roianalog*> ROI;
|
||||
};
|
||||
|
||||
|
||||
class ClassFlowAnalog :
|
||||
public ClassFlowImage
|
||||
{
|
||||
protected:
|
||||
// std::vector<roianalog*> ROI;
|
||||
std::vector<analog*> ANALOG;
|
||||
|
||||
string cnnmodelfile;
|
||||
int modelxsize, modelysize;
|
||||
int ZeigerEval(float zahl, int ziffer_vorgaenger);
|
||||
bool SaveAllFiles;
|
||||
|
||||
|
||||
ClassFlowAlignment* flowpostalignment;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
|
||||
|
||||
public:
|
||||
bool extendedResolution;
|
||||
|
||||
ClassFlowAnalog(std::vector<ClassFlow*>* lfc);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getHTMLSingleStep(string host);
|
||||
string getReadout(int _analog);
|
||||
|
||||
void DrawROI(CImageBasis *_zw);
|
||||
|
||||
bool doNeuralNetwork(string time);
|
||||
bool doAlignAndCut(string time);
|
||||
std::vector<HTMLInfo*> GetHTMLInfo();
|
||||
int AnzahlROIs(int _analog);
|
||||
|
||||
int getAnzahlANALOG();
|
||||
analog* GetANALOG(int _analog);
|
||||
analog* GetANALOG(string _name, bool _create);
|
||||
analog* FindANALOG(string _name_number);
|
||||
string getNameANALOG(int _analog);
|
||||
|
||||
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
|
||||
|
||||
|
||||
string name(){return "ClassFlowAnalog";};
|
||||
};
|
||||
|
||||
665
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
Normal file
665
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
Normal file
@@ -0,0 +1,665 @@
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <iomanip>
|
||||
#include <sys/types.h>
|
||||
#include <sstream> // std::stringstream
|
||||
|
||||
#include "CTfLiteClass.h"
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
static const char* TAG = "flow_analog";
|
||||
|
||||
bool debugdetailgeneral = false;
|
||||
|
||||
ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype) : ClassFlowImage(NULL, TAG)
|
||||
{
|
||||
string cnnmodelfile = "";
|
||||
modelxsize = 1;
|
||||
modelysize = 1;
|
||||
ListFlowControll = NULL;
|
||||
previousElement = NULL;
|
||||
SaveAllFiles = false;
|
||||
disabled = false;
|
||||
// extendedResolution = false;
|
||||
isLogImageSelect = false;
|
||||
CNNType = AutoDetect;
|
||||
CNNType = _cnntype;
|
||||
flowpostalignment = _flowalign;
|
||||
}
|
||||
|
||||
/*
|
||||
int ClassFlowCNNGeneral::AnzahlROIs(int _analog = 0)
|
||||
{
|
||||
int zw = GENERAL[_analog]->ROI.size();
|
||||
if (extendedResolution && (CNNType != Digital)) zw++; // da letzte Ziffer inkl Nachhkomma, es sei denn, das Nachkomma gibt es nicht (Digital)
|
||||
return zw;
|
||||
}
|
||||
*/
|
||||
|
||||
string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution = false)
|
||||
{
|
||||
string result = "";
|
||||
if (GENERAL[_analog]->ROI.size() == 0)
|
||||
return result;
|
||||
|
||||
if (CNNType == Analogue)
|
||||
{
|
||||
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10) + 10) % 10;
|
||||
|
||||
int prev = -1;
|
||||
|
||||
prev = ZeigerEval(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev);
|
||||
result = std::to_string(prev);
|
||||
|
||||
if (_extendedResolution && (CNNType != Digital))
|
||||
result = result + std::to_string(ergebnis_nachkomma);
|
||||
|
||||
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
|
||||
{
|
||||
prev = ZeigerEval(GENERAL[_analog]->ROI[i]->result_float, prev);
|
||||
result = std::to_string(prev) + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (CNNType == Digital)
|
||||
{
|
||||
for (int i = 0; i < GENERAL[_analog]->ROI.size(); ++i)
|
||||
{
|
||||
if (GENERAL[_analog]->ROI[i]->result_klasse >= 10)
|
||||
result = result + "N";
|
||||
else
|
||||
result = result + std::to_string(GENERAL[_analog]->ROI[i]->result_klasse);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (CNNType == DigitalHyprid)
|
||||
{
|
||||
// int ergebnis_nachkomma = -1;
|
||||
int zif_akt = -1;
|
||||
|
||||
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
|
||||
if (zahl >= 0) // NaN?
|
||||
{
|
||||
if (_extendedResolution)
|
||||
{
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
|
||||
|
||||
result = std::to_string(ergebnis_vorkomma) + std::to_string(ergebnis_nachkomma);
|
||||
zif_akt = ergebnis_vorkomma;
|
||||
}
|
||||
else
|
||||
{
|
||||
zif_akt = ZeigerEvalHybrid(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, -1, -1);
|
||||
result = std::to_string(zif_akt);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "N";
|
||||
if (_extendedResolution && (CNNType != Digital))
|
||||
result = "NN";
|
||||
}
|
||||
|
||||
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
|
||||
{
|
||||
if (GENERAL[_analog]->ROI[i]->result_float >= 0)
|
||||
{
|
||||
zif_akt = ZeigerEvalHybrid(GENERAL[_analog]->ROI[i]->result_float, GENERAL[_analog]->ROI[i+1]->result_float, zif_akt);
|
||||
result = std::to_string(zif_akt) + result;
|
||||
}
|
||||
else
|
||||
{
|
||||
zif_akt = -1;
|
||||
result = "N" + result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger)
|
||||
{
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
// int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
|
||||
|
||||
if (zahl_vorgaenger < 0) // keine Vorzahl vorhanden !!! --> Runde die Zahl
|
||||
{
|
||||
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||
return ((int) round(zahl) + 10) % 10;
|
||||
else
|
||||
return ((int) trunc(zahl) + 10) % 10;
|
||||
}
|
||||
|
||||
if (zahl_vorgaenger > 9.2) // Ziffernwechsel beginnt
|
||||
{
|
||||
if (eval_vorgaenger == 0) // Wechsel hat schon stattgefunden
|
||||
{
|
||||
return ((int) round(zahl) + 10) % 10; // Annahme, dass die neue Zahl schon in der Nähe des Ziels ist
|
||||
}
|
||||
else
|
||||
{
|
||||
if (zahl_vorgaenger <= 9.5) // Wechsel startet gerade, aber beginnt erst
|
||||
{
|
||||
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||
return ((int) round(zahl) + 10) % 10;
|
||||
else
|
||||
return ((int) trunc(zahl) + 10) % 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((int) trunc(zahl) + 10) % 10; // Wechsel schon weiter fortgeschritten, d.h. über 2 als Nachkomma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||
return ((int) round(zahl) + 10) % 10;
|
||||
|
||||
return ((int) trunc(zahl) + 10) % 10;
|
||||
}
|
||||
|
||||
int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
|
||||
{
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10) + 10) % 10;
|
||||
int ergebnis_vorkomma = ((int) floor(zahl) + 10) % 10;
|
||||
int ergebnis, ergebnis_rating;
|
||||
|
||||
if (ziffer_vorgaenger == -1)
|
||||
return ergebnis_vorkomma % 10;
|
||||
|
||||
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
|
||||
if (ergebnis_nachkomma >= 5)
|
||||
ergebnis_rating-=5;
|
||||
else
|
||||
ergebnis_rating+=5;
|
||||
ergebnis = (int) round(zahl);
|
||||
if (ergebnis_rating < 0)
|
||||
ergebnis-=1;
|
||||
if (ergebnis == -1)
|
||||
ergebnis+=10;
|
||||
|
||||
ergebnis = (ergebnis + 10) % 10;
|
||||
return ergebnis;
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> zerlegt;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
|
||||
if ((toUpper(aktparamgraph) != "[ANALOG]") && (toUpper(aktparamgraph) != ";[ANALOG]")
|
||||
&& (toUpper(aktparamgraph) != "[DIGIT]") && (toUpper(aktparamgraph) != ";[DIGIT]")
|
||||
&& (toUpper(aktparamgraph) != "[DIGITS]") && (toUpper(aktparamgraph) != ";[DIGITS]")
|
||||
) // Paragraph passt nicht
|
||||
return false;
|
||||
|
||||
|
||||
/*
|
||||
if ((aktparamgraph.compare("[Analog]") != 0) && (aktparamgraph.compare(";[Analog]") != 0)
|
||||
&& (aktparamgraph.compare("[Digit]") != 0) && (aktparamgraph.compare(";[Digit]"))) // Paragraph passt nicht
|
||||
return false;
|
||||
*/
|
||||
|
||||
if (aktparamgraph[0] == ';')
|
||||
{
|
||||
disabled = true;
|
||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
printf("[Analog/Digit] is disabled !!!\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->LogImageLocation = "/sdcard" + zerlegt[1];
|
||||
this->isLogImage = true;
|
||||
}
|
||||
if ((zerlegt[0] == "LogImageSelect") && (zerlegt.size() > 1))
|
||||
{
|
||||
LogImageSelect = zerlegt[1];
|
||||
isLogImageSelect = true;
|
||||
}
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "LOGFILERETENTIONINDAYS") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->logfileRetentionInDays = std::stoi(zerlegt[1]);
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "MODELTYPE") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "DIGITHYPRID")
|
||||
CNNType = DigitalHyprid;
|
||||
}
|
||||
|
||||
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->cnnmodelfile = zerlegt[1];
|
||||
}
|
||||
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
||||
{
|
||||
this->modelxsize = std::stoi(zerlegt[1]);
|
||||
this->modelysize = std::stoi(zerlegt[2]);
|
||||
}
|
||||
if (zerlegt.size() >= 5)
|
||||
{
|
||||
general* _analog = GetGENERAL(zerlegt[0], true);
|
||||
roi* neuroi = _analog->ROI[_analog->ROI.size()-1];
|
||||
neuroi->posx = std::stoi(zerlegt[1]);
|
||||
neuroi->posy = std::stoi(zerlegt[2]);
|
||||
neuroi->deltax = std::stoi(zerlegt[3]);
|
||||
neuroi->deltay = std::stoi(zerlegt[4]);
|
||||
neuroi->result_float = -1;
|
||||
neuroi->image = NULL;
|
||||
neuroi->image_org = NULL;
|
||||
}
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
SaveAllFiles = true;
|
||||
}
|
||||
|
||||
/*
|
||||
if ((toUpper(zerlegt[0]) == "EXTENDEDRESOLUTION") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
extendedResolution = true;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
|
||||
GENERAL[_ana]->ROI[i]->image_org = new CImageBasis(GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
general* ClassFlowCNNGeneral::FindGENERAL(string _name_number)
|
||||
{
|
||||
for (int i = 0; i < GENERAL.size(); ++i)
|
||||
if (GENERAL[i]->name == _name_number)
|
||||
return GENERAL[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true)
|
||||
{
|
||||
string _analog, _roi;
|
||||
int _pospunkt = _name.find_first_of(".");
|
||||
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_analog = _name.substr(0, _pospunkt);
|
||||
_roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
_analog = "default";
|
||||
_roi = _name;
|
||||
}
|
||||
|
||||
general *_ret = NULL;
|
||||
|
||||
for (int i = 0; i < GENERAL.size(); ++i)
|
||||
if (GENERAL[i]->name == _analog)
|
||||
_ret = GENERAL[i];
|
||||
|
||||
if (!_create) // nicht gefunden und soll auch nicht erzeugt werden
|
||||
return _ret;
|
||||
|
||||
if (_ret == NULL)
|
||||
{
|
||||
_ret = new general;
|
||||
_ret->name = _analog;
|
||||
GENERAL.push_back(_ret);
|
||||
}
|
||||
|
||||
roi* neuroi = new roi;
|
||||
neuroi->name = _roi;
|
||||
_ret->ROI.push_back(neuroi);
|
||||
|
||||
printf("GetGENERAL - GENERAL %s - roi %s\n", _analog.c_str(), _roi.c_str());
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
string ClassFlowCNNGeneral::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result, zw;
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
|
||||
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
|
||||
result = result + "Analog Pointers: <p> ";
|
||||
|
||||
htmlinfo = GetHTMLInfo();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
|
||||
zw = stream.str();
|
||||
|
||||
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ClassFlowCNNGeneral::doFlow(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
if (!doAlignAndCut(time)){
|
||||
return false;
|
||||
};
|
||||
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::doFlow nach Alignment");
|
||||
|
||||
doNeuralNetwork(time);
|
||||
|
||||
RemoveOldLogs();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::doAlignAndCut(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
printf("General %d - Align&Cut\n", i);
|
||||
|
||||
caic->CutAndSave(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, GENERAL[_ana]->ROI[i]->image_org);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||
else
|
||||
GENERAL[_ana]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg"));
|
||||
}
|
||||
|
||||
GENERAL[_ana]->ROI[i]->image_org->Resize(modelxsize, modelysize, GENERAL[_ana]->ROI[i]->image);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||
else
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
|
||||
{
|
||||
if (CNNType == Analogue)
|
||||
{
|
||||
int r = 0;
|
||||
int g = 255;
|
||||
int b = 0;
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
_zw->drawRect(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, r, g, b, 1);
|
||||
_zw->drawCircle((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), r, g, b, 2);
|
||||
_zw->drawLine((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) GENERAL[_ana]->ROI[i]->posy, (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay), r, g, b, 2);
|
||||
_zw->drawLine((int) GENERAL[_ana]->ROI[i]->posx, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int _dig = 0; _dig < GENERAL.size(); ++_dig)
|
||||
for (int i = 0; i < GENERAL[_dig]->ROI.size(); ++i)
|
||||
_zw->drawRect(GENERAL[_dig]->ROI[i]->posx, GENERAL[_dig]->ROI[i]->posy, GENERAL[_dig]->ROI[i]->deltax, GENERAL[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
string logPath = CreateLogFolder(time);
|
||||
|
||||
CTfLiteClass *tflite = new CTfLiteClass;
|
||||
string zwcnn = "/sdcard" + cnnmodelfile;
|
||||
zwcnn = FormatFileName(zwcnn);
|
||||
printf(zwcnn.c_str());printf("\n");
|
||||
if (!tflite->LoadModel(zwcnn)) {
|
||||
printf("Can't read model file /sdcard%s\n", cnnmodelfile.c_str());
|
||||
LogFile.WriteToFile("Cannot load model");
|
||||
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
tflite->MakeAllocate();
|
||||
|
||||
if (CNNType == AutoDetect)
|
||||
{
|
||||
int _anzoutputdimensions = tflite->GetAnzOutPut();
|
||||
switch (_anzoutputdimensions)
|
||||
{
|
||||
case 2:
|
||||
CNNType = Analogue;
|
||||
printf("TFlite-Type set to Analogue\n");
|
||||
break;
|
||||
case 11:
|
||||
CNNType = Digital;
|
||||
printf("TFlite-Type set to Digital\n");
|
||||
break;
|
||||
case 22:
|
||||
CNNType = DigitalHyprid;
|
||||
printf("TFlite-Type set to DigitalHyprid\n");
|
||||
break;
|
||||
default:
|
||||
printf("ERROR ERROR ERROR - tflite passt nicht zur Firmware - ERROR ERROR ERROR\n");
|
||||
}
|
||||
// flowpostprocessing->UpdateNachkommaDecimalShift();
|
||||
}
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
{
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
printf("General %d - TfLite\n", i);
|
||||
|
||||
switch (CNNType) {
|
||||
case Analogue:
|
||||
{
|
||||
float f1, f2;
|
||||
f1 = 0; f2 = 0;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
|
||||
|
||||
f1 = tflite->GetOutputValue(0);
|
||||
f2 = tflite->GetOutputValue(1);
|
||||
float result = fmod(atan2(f1, f2) / (M_PI * 2) + 2, 1);
|
||||
GENERAL[_ana]->ROI[i]->result_float = result * 10;
|
||||
printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
|
||||
if (isLogImage)
|
||||
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
} break;
|
||||
case Digital:
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->result_klasse = 0;
|
||||
GENERAL[_ana]->ROI[i]->result_klasse = tflite->GetClassFromImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
printf("Result General(Digit)%i: %d\n", i, GENERAL[_ana]->ROI[i]->result_klasse);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
|
||||
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case DigitalHyprid:
|
||||
{
|
||||
int _num, _nachkomma;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
|
||||
|
||||
_num = tflite->GetOutClassification(0, 10);
|
||||
_nachkomma = tflite->GetOutClassification(11, 21);
|
||||
|
||||
|
||||
string _zwres = "Nach Invoke - Nummer: " + to_string(_num) + " Nachkomma: " + to_string(_nachkomma);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
|
||||
|
||||
if ((_num == 10) || (_nachkomma == 10)) // NaN detektiert
|
||||
GENERAL[_ana]->ROI[i]->result_float = -1;
|
||||
else
|
||||
GENERAL[_ana]->ROI[i]->result_float = fmod((double) _num + (((double)_nachkomma)-5)/10 + (double) 10, 10);
|
||||
|
||||
printf("Result General(DigitalHyprid)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
|
||||
_zwres = "Result General(DigitalHyprid)" + to_string(i) + ": " + to_string(GENERAL[_ana]->ROI[i]->result_float);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
|
||||
|
||||
if (isLogImage)
|
||||
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete tflite;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
|
||||
{
|
||||
// if (extendedResolution && !(CNNType == Digital))
|
||||
if (!(CNNType == Digital))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowCNNGeneral::GetHTMLInfo()
|
||||
{
|
||||
std::vector<HTMLInfo*> result;
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||
else
|
||||
GENERAL[_ana]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp"));
|
||||
|
||||
|
||||
HTMLInfo *zw = new HTMLInfo;
|
||||
if (GENERAL[_ana]->name == "default")
|
||||
{
|
||||
zw->filename = GENERAL[_ana]->ROI[i]->name + ".bmp";
|
||||
zw->filename_org = GENERAL[_ana]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
else
|
||||
{
|
||||
zw->filename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".bmp";
|
||||
zw->filename_org = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
|
||||
if (CNNType == Digital)
|
||||
zw->val = GENERAL[_ana]->ROI[i]->result_klasse;
|
||||
else
|
||||
zw->val = GENERAL[_ana]->ROI[i]->result_float;
|
||||
zw->image = GENERAL[_ana]->ROI[i]->image;
|
||||
zw->image_org = GENERAL[_ana]->ROI[i]->image_org;
|
||||
|
||||
// printf("Push %s\n", zw->filename.c_str());
|
||||
|
||||
result.push_back(zw);
|
||||
}
|
||||
|
||||
// printf("größe: %d\n", result.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ClassFlowCNNGeneral::getAnzahlGENERAL()
|
||||
{
|
||||
return GENERAL.size();
|
||||
}
|
||||
|
||||
string ClassFlowCNNGeneral::getNameGENERAL(int _analog)
|
||||
{
|
||||
if (_analog < GENERAL.size())
|
||||
return GENERAL[_analog]->name;
|
||||
|
||||
return "GENERAL DOES NOT EXIST";
|
||||
}
|
||||
|
||||
general* ClassFlowCNNGeneral::GetGENERAL(int _analog)
|
||||
{
|
||||
if (_analog < GENERAL.size())
|
||||
return GENERAL[_analog];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClassFlowCNNGeneral::UpdateNameNumbers(std::vector<std::string> *_name_numbers)
|
||||
{
|
||||
for (int _dig = 0; _dig < GENERAL.size(); _dig++)
|
||||
{
|
||||
std::string _name = GENERAL[_dig]->name;
|
||||
bool found = false;
|
||||
for (int i = 0; i < (*_name_numbers).size(); ++i)
|
||||
{
|
||||
if ((*_name_numbers)[i] == _name)
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
(*_name_numbers).push_back(_name);
|
||||
}
|
||||
}
|
||||
72
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
Normal file
72
code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef __CLASSCNNGENERAL__
|
||||
#define __CLASSCNNGENERAL__
|
||||
|
||||
#include"ClassFlowDefineTypes.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
// #include "ClassFlowPostProcessing.h"
|
||||
|
||||
|
||||
enum t_CNNType {
|
||||
AutoDetect,
|
||||
Analogue,
|
||||
Digital,
|
||||
DigitalHyprid,
|
||||
None
|
||||
};
|
||||
|
||||
class ClassFlowCNNGeneral :
|
||||
public ClassFlowImage
|
||||
{
|
||||
protected:
|
||||
t_CNNType CNNType;
|
||||
std::vector<general*> GENERAL;
|
||||
|
||||
string cnnmodelfile;
|
||||
int modelxsize, modelysize;
|
||||
bool isLogImageSelect;
|
||||
string LogImageSelect;
|
||||
ClassFlowAlignment* flowpostalignment;
|
||||
// ClassFlowPostProcessing *flowpostprocessing = NULL;
|
||||
bool SaveAllFiles;
|
||||
// bool extendedResolution;
|
||||
|
||||
int ZeigerEval(float zahl, int ziffer_vorgaenger);
|
||||
int ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger);
|
||||
|
||||
|
||||
bool doNeuralNetwork(string time);
|
||||
bool doAlignAndCut(string time);
|
||||
|
||||
public:
|
||||
ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype = AutoDetect);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
|
||||
string getHTMLSingleStep(string host);
|
||||
string getReadout(int _analog, bool _extendedResolution);
|
||||
|
||||
void DrawROI(CImageBasis *_zw);
|
||||
|
||||
std::vector<HTMLInfo*> GetHTMLInfo();
|
||||
|
||||
// int AnzahlROIs(int _analog);
|
||||
int getAnzahlGENERAL();
|
||||
general* GetGENERAL(int _analog);
|
||||
general* GetGENERAL(string _name, bool _create);
|
||||
general* FindGENERAL(string _name_number);
|
||||
string getNameGENERAL(int _analog);
|
||||
|
||||
bool isExtendedResolution(int _number = 0);
|
||||
|
||||
// void setPostprocessing(ClassFlowPostProcessing *_fpp){flowpostprocessing = _fpp;};
|
||||
|
||||
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
|
||||
|
||||
t_CNNType getCNNType(){return CNNType;};
|
||||
|
||||
string name(){return "ClassFlowCNNGeneral";};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,12 +6,23 @@
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
#include "time_sntp.h"
|
||||
#include "Helper.h"
|
||||
#include "server_ota.h"
|
||||
|
||||
|
||||
//#include "CImg.h"
|
||||
|
||||
#include "server_help.h"
|
||||
|
||||
//#define DEBUG_DETAIL_ON
|
||||
@@ -32,10 +43,10 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
if ((_stepname.compare(0, 7, "[Digits") == 0) || (_stepname.compare(0, 8, ";[Digits") == 0)) {
|
||||
// if ((_stepname.compare("[Digits]") == 0) || (_stepname.compare(";[Digits]") == 0)){
|
||||
// printf("Digits!!!\n");
|
||||
_classname = "ClassFlowDigit";
|
||||
_classname = "ClassFlowCNNGeneral";
|
||||
}
|
||||
if ((_stepname.compare("[Analog]") == 0) || (_stepname.compare(";[Analog]") == 0)){
|
||||
_classname = "ClassFlowAnalog";
|
||||
_classname = "ClassFlowCNNGeneral";
|
||||
}
|
||||
if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)){
|
||||
_classname = "ClassFlowMQTT";
|
||||
@@ -51,11 +62,33 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||
|
||||
std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
||||
{
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare("ClassFlowDigit") == 0)
|
||||
return ((ClassFlowDigit*) (FlowControll[i]))->GetHTMLInfo();
|
||||
if (_input.compare("ClassFlowMakeImage") == 0)
|
||||
return ("Take Image");
|
||||
if (_input.compare("ClassFlowAlignment") == 0)
|
||||
return ("Aligning");
|
||||
//if (_input.compare("ClassFlowAnalog") == 0)
|
||||
// return ("Analog ROIs");
|
||||
if (_input.compare("ClassFlowCNNGeneral") == 0)
|
||||
return ("Digitalization of ROIs");
|
||||
if (_input.compare("ClassFlowMQTT") == 0)
|
||||
return ("Sending MQTT");
|
||||
if (_input.compare("ClassFlowPostProcessing") == 0)
|
||||
return ("Processing");
|
||||
|
||||
return "Unkown Status";
|
||||
}
|
||||
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||
{
|
||||
if (flowdigit)
|
||||
{
|
||||
printf("ClassFlowControll::GetAllDigital - flowdigit != NULL\n");
|
||||
return flowdigit->GetHTMLInfo();
|
||||
}
|
||||
|
||||
std::vector<HTMLInfo*> empty;
|
||||
return empty;
|
||||
@@ -63,14 +96,43 @@ std::vector<HTMLInfo*> ClassFlowControll::GetAllDigital()
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowControll::GetAllAnalog()
|
||||
{
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare("ClassFlowAnalog") == 0)
|
||||
return ((ClassFlowAnalog*) (FlowControll[i]))->GetHTMLInfo();
|
||||
if (flowanalog)
|
||||
return flowanalog->GetHTMLInfo();
|
||||
|
||||
std::vector<HTMLInfo*> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
t_CNNType ClassFlowControll::GetTypeDigital()
|
||||
{
|
||||
if (flowdigit)
|
||||
return flowdigit->getCNNType();
|
||||
|
||||
return t_CNNType::None;
|
||||
}
|
||||
|
||||
t_CNNType ClassFlowControll::GetTypeAnalog()
|
||||
{
|
||||
if (flowanalog)
|
||||
return flowanalog->getCNNType();
|
||||
|
||||
return t_CNNType::None;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
string ClassFlowControll::GetMQTTMainTopic()
|
||||
{
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare("ClassFlowMQTT") == 0)
|
||||
return ((ClassFlowMQTT*) (FlowControll[i]))->GetMQTTMainTopic();
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClassFlowControll::SetInitialParameter(void)
|
||||
{
|
||||
@@ -82,7 +144,7 @@ void ClassFlowControll::SetInitialParameter(void)
|
||||
flowpostprocessing = NULL;
|
||||
disabled = false;
|
||||
aktRunNr = 0;
|
||||
aktstatus = "Startup";
|
||||
aktstatus = "Booting ...";
|
||||
|
||||
}
|
||||
|
||||
@@ -110,20 +172,20 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
}
|
||||
if (toUpper(_type).compare("[ANALOG]") == 0)
|
||||
{
|
||||
cfc = new ClassFlowAnalog(&FlowControll);
|
||||
flowanalog = (ClassFlowAnalog*) cfc;
|
||||
cfc = new ClassFlowCNNGeneral(flowalignment);
|
||||
flowanalog = (ClassFlowCNNGeneral*) cfc;
|
||||
}
|
||||
if (toUpper(_type).compare(0, 7, "[DIGITS") == 0)
|
||||
{
|
||||
cfc = new ClassFlowDigit(&FlowControll);
|
||||
flowdigit = (ClassFlowDigit*) cfc;
|
||||
cfc = new ClassFlowCNNGeneral(flowalignment);
|
||||
flowdigit = (ClassFlowCNNGeneral*) cfc;
|
||||
}
|
||||
if (toUpper(_type).compare("[MQTT]") == 0)
|
||||
cfc = new ClassFlowMQTT(&FlowControll);
|
||||
|
||||
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
|
||||
{
|
||||
cfc = new ClassFlowPostProcessing(&FlowControll);
|
||||
cfc = new ClassFlowPostProcessing(&FlowControll, flowanalog, flowdigit);
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) cfc;
|
||||
}
|
||||
|
||||
@@ -168,14 +230,17 @@ void ClassFlowControll::InitFlow(std::string config)
|
||||
cfc = CreateClassFlow(line);
|
||||
if (cfc)
|
||||
{
|
||||
printf("Start ReadParameter\n");
|
||||
printf("Start ReadParameter (%s)\n", line.c_str());
|
||||
cfc->ReadParameter(pFile, line);
|
||||
}
|
||||
else
|
||||
{
|
||||
fgets(zw, 1024, pFile);
|
||||
printf("%s", zw);
|
||||
line = std::string(zw);
|
||||
line = "";
|
||||
if (fgets(zw, 1024, pFile) && !feof(pFile))
|
||||
{
|
||||
printf("Read: %s", zw);
|
||||
line = std::string(zw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,8 +248,8 @@ void ClassFlowControll::InitFlow(std::string config)
|
||||
|
||||
}
|
||||
|
||||
std::string ClassFlowControll::getActStatus(){
|
||||
return aktstatus;
|
||||
std::string* ClassFlowControll::getActStatus(){
|
||||
return &aktstatus;
|
||||
}
|
||||
|
||||
void ClassFlowControll::doFlowMakeImageOnly(string time){
|
||||
@@ -193,9 +258,9 @@ void ClassFlowControll::doFlowMakeImageOnly(string time){
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
{
|
||||
if (FlowControll[i]->name() == "ClassFlowMakeImage") {
|
||||
zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||
aktstatus = zw_time + ": " + FlowControll[i]->name();
|
||||
string zw = "FlowControll.doFlowMakeImageOnly - " + FlowControll[i]->name();
|
||||
// zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||
zw_time = gettimestring("%H:%M:%S");
|
||||
aktstatus = TranslateAktstatus(FlowControll[i]->name()) + " (" + zw_time + ")";
|
||||
FlowControll[i]->doFlow(time);
|
||||
}
|
||||
}
|
||||
@@ -215,8 +280,11 @@ bool ClassFlowControll::doFlow(string time)
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
{
|
||||
zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||
aktstatus = zw_time + ": " + FlowControll[i]->name();
|
||||
zw_time = gettimestring("%H:%M:%S");
|
||||
aktstatus = TranslateAktstatus(FlowControll[i]->name()) + " (" + zw_time + ")";
|
||||
|
||||
// zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||
// aktstatus = zw_time + ": " + FlowControll[i]->name();
|
||||
|
||||
|
||||
string zw = "FlowControll.doFlow - " + FlowControll[i]->name();
|
||||
@@ -225,7 +293,7 @@ bool ClassFlowControll::doFlow(string time)
|
||||
if (!FlowControll[i]->doFlow(time)){
|
||||
repeat++;
|
||||
LogFile.WriteToFile("Fehler im vorheriger Schritt - wird zum " + to_string(repeat) + ". Mal wiederholt");
|
||||
i = -1; // vorheriger Schritt muss wiederholt werden (vermutlich Bilder aufnehmen)
|
||||
if (i) i -= 1; // vorheriger Schritt muss wiederholt werden (vermutlich Bilder aufnehmen)
|
||||
result = false;
|
||||
if (repeat > 5) {
|
||||
LogFile.WriteToFile("Wiederholung 5x nicht erfolgreich --> reboot");
|
||||
@@ -243,53 +311,49 @@ bool ClassFlowControll::doFlow(string time)
|
||||
#endif
|
||||
|
||||
}
|
||||
zw_time = gettimestring("%Y%m%d-%H%M%S");
|
||||
aktstatus = zw_time + ": Flow is done";
|
||||
zw_time = gettimestring("%H:%M:%S");
|
||||
aktstatus = "Flow finished (" + zw_time + ")";
|
||||
return result;
|
||||
}
|
||||
|
||||
void ClassFlowControll::UpdateAktStatus(std::string _flow)
|
||||
{
|
||||
aktstatus = gettimestring("%Y%m%d-%H%M%S");
|
||||
aktstatus = aktstatus + "\t" + std::to_string(aktRunNr) + "\t";
|
||||
|
||||
if (_flow == "ClassFlowMakeImage")
|
||||
aktstatus = aktstatus + "Taking Raw Image";
|
||||
else
|
||||
if (_flow == "ClassFlowAlignment")
|
||||
aktstatus = aktstatus + "Aligning Image";
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getReadoutAll(int _type)
|
||||
{
|
||||
std::vector<NumberPost*> numbers = flowpostprocessing->GetNumbers();
|
||||
std::string out = "";
|
||||
|
||||
for (int i = 0; i < numbers.size(); ++i)
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
out = out + numbers[i]->name + "\t";
|
||||
switch (_type) {
|
||||
case READOUT_TYPE_VALUE:
|
||||
out = out + numbers[i]->ReturnValue;
|
||||
break;
|
||||
case READOUT_TYPE_PREVALUE:
|
||||
out = out + numbers[i]->ReturnPreValue;
|
||||
break;
|
||||
case READOUT_TYPE_RAWVALUE:
|
||||
out = out + numbers[i]->ReturnRawValue;
|
||||
break;
|
||||
case READOUT_TYPE_ERROR:
|
||||
out = out + numbers[i]->ErrorMessageText;
|
||||
break;
|
||||
}
|
||||
if (i < numbers.size()-1)
|
||||
out = out + "\r\n";
|
||||
}
|
||||
std::vector<NumberPost*> *numbers = flowpostprocessing->GetNumbers();
|
||||
|
||||
// printf("OUT: %s", out.c_str());
|
||||
for (int i = 0; i < (*numbers).size(); ++i)
|
||||
{
|
||||
out = out + (*numbers)[i]->name + "\t";
|
||||
switch (_type) {
|
||||
case READOUT_TYPE_VALUE:
|
||||
out = out + (*numbers)[i]->ReturnValueNoError;
|
||||
break;
|
||||
case READOUT_TYPE_PREVALUE:
|
||||
if (flowpostprocessing->PreValueUse)
|
||||
{
|
||||
if ((*numbers)[i]->PreValueOkay)
|
||||
out = out + (*numbers)[i]->ReturnPreValue;
|
||||
else
|
||||
out = out + "PreValue too old";
|
||||
}
|
||||
else
|
||||
out = out + "PreValue deactivated";
|
||||
break;
|
||||
case READOUT_TYPE_RAWVALUE:
|
||||
out = out + (*numbers)[i]->ReturnRawValue;
|
||||
break;
|
||||
case READOUT_TYPE_ERROR:
|
||||
out = out + (*numbers)[i]->ErrorMessageText;
|
||||
break;
|
||||
}
|
||||
if (i < (*numbers).size()-1)
|
||||
out = out + "\r\n";
|
||||
}
|
||||
// printf("OUT: %s", out.c_str());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -328,7 +392,7 @@ string ClassFlowControll::GetPrevalue(std::string _number)
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers)
|
||||
std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern)
|
||||
{
|
||||
float zw;
|
||||
char* p;
|
||||
@@ -350,7 +414,7 @@ std::string ClassFlowControll::UpdatePrevalue(std::string _newvalue, std::string
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
flowpostprocessing->SetPreValue(zw, _numbers);
|
||||
flowpostprocessing->SetPreValue(zw, _numbers, _extern);
|
||||
return _newvalue;
|
||||
}
|
||||
|
||||
@@ -442,6 +506,7 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int ClassFlowControll::CleanTempFolder() {
|
||||
const char* folderPath = "/sdcard/img_tmp";
|
||||
|
||||
@@ -497,53 +562,59 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
{
|
||||
_send = flowalignment->ImageBasis;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (_fn == "alg_roi.jpg")
|
||||
else
|
||||
{
|
||||
CImageBasis* _imgzw = new CImageBasis(flowalignment->ImageBasis);
|
||||
flowalignment->DrawRef(_imgzw);
|
||||
if (flowdigit) flowdigit->DrawROI(_imgzw);
|
||||
if (flowanalog) flowanalog->DrawROI(_imgzw);
|
||||
_send = _imgzw;
|
||||
Dodelete = true;
|
||||
}
|
||||
if (_fn == "alg_roi.jpg")
|
||||
{
|
||||
CImageBasis* _imgzw = new CImageBasis(flowalignment->ImageBasis);
|
||||
flowalignment->DrawRef(_imgzw);
|
||||
if (flowdigit) flowdigit->DrawROI(_imgzw);
|
||||
if (flowanalog) flowanalog->DrawROI(_imgzw);
|
||||
_send = _imgzw;
|
||||
Dodelete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
htmlinfo = GetAllDigital();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
htmlinfo = GetAllDigital();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
if (!_send)
|
||||
{
|
||||
htmlinfo = GetAllAnalog();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
htmlinfo = GetAllAnalog();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
{
|
||||
if (htmlinfo[i]->image)
|
||||
_send = htmlinfo[i]->image;
|
||||
}
|
||||
}
|
||||
if (_fn == htmlinfo[i]->filename_org)
|
||||
{
|
||||
if (htmlinfo[i]->image_org)
|
||||
_send = htmlinfo[i]->image_org;
|
||||
}
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
if (_send)
|
||||
{
|
||||
@@ -561,4 +632,37 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getJSON()
|
||||
{
|
||||
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||
|
||||
std::string json="{\n";
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
json += "\"" + (*NUMBERS)[i]->name + "\":\n";
|
||||
json += " {\n";
|
||||
if ((*NUMBERS)[i]->ReturnValueNoError.length() > 0)
|
||||
json += " \"value\": " + (*NUMBERS)[i]->ReturnValueNoError + ",\n";
|
||||
else
|
||||
json += " \"value\": \"\",\n";
|
||||
json += " \"raw\": \"" + (*NUMBERS)[i]->ReturnRawValue + "\",\n";
|
||||
json += " \"error\": \"" + (*NUMBERS)[i]->ErrorMessageText + "\",\n";
|
||||
if ((*NUMBERS)[i]->ReturnRateValue.length() > 0)
|
||||
json += " \"rate\": " + (*NUMBERS)[i]->ReturnRateValue + ",\n";
|
||||
else
|
||||
json += " \"rate\": \"\",\n";
|
||||
|
||||
json += " \"timestamp\": \"" + (*NUMBERS)[i]->timeStamp + "\"\n";
|
||||
if ((i+1) < (*NUMBERS).size())
|
||||
json += " },\n";
|
||||
else
|
||||
json += " }\n";
|
||||
}
|
||||
json += "}";
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
#pragma once
|
||||
#ifndef __FLOWCONTROLL__
|
||||
#define __FLOWCONTROLL__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "ClassFlowMakeImage.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "ClassFlowDigit.h"
|
||||
#include "ClassFlowAnalog.h"
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "ClassFlowMQTT.h"
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
|
||||
|
||||
#define READOUT_TYPE_VALUE 0
|
||||
@@ -24,8 +25,9 @@ protected:
|
||||
std::vector<ClassFlow*> FlowControll;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
ClassFlowAlignment* flowalignment;
|
||||
ClassFlowAnalog* flowanalog;
|
||||
ClassFlowDigit* flowdigit;
|
||||
ClassFlowCNNGeneral* flowanalog;
|
||||
ClassFlowCNNGeneral* flowdigit;
|
||||
// ClassFlowDigit* flowdigit;
|
||||
ClassFlowMakeImage* flowmakeimage;
|
||||
ClassFlow* CreateClassFlow(std::string _type);
|
||||
|
||||
@@ -36,8 +38,6 @@ protected:
|
||||
std::string aktstatus;
|
||||
int aktRunNr;
|
||||
|
||||
void UpdateAktStatus(std::string _flow);
|
||||
|
||||
public:
|
||||
void InitFlow(std::string config);
|
||||
bool doFlow(string time);
|
||||
@@ -45,9 +45,14 @@ public:
|
||||
bool getStatusSetupModus(){return SetupModeActive;};
|
||||
string getReadout(bool _rawvalue, bool _noerror);
|
||||
string getReadoutAll(int _type);
|
||||
string UpdatePrevalue(std::string _newvalue, std::string _numbers);
|
||||
string UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern);
|
||||
string GetPrevalue(std::string _number = "");
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
string getJSON();
|
||||
|
||||
string TranslateAktstatus(std::string _input);
|
||||
|
||||
string GetMQTTMainTopic();
|
||||
|
||||
esp_err_t GetJPGStream(std::string _fn, httpd_req_t *req);
|
||||
esp_err_t SendRawJPG(httpd_req_t *req);
|
||||
@@ -56,14 +61,19 @@ public:
|
||||
|
||||
bool isAutoStart(long &_intervall);
|
||||
|
||||
std::string getActStatus();
|
||||
std::string* getActStatus();
|
||||
|
||||
std::vector<HTMLInfo*> GetAllDigital();
|
||||
std::vector<HTMLInfo*> GetAllAnalog();
|
||||
|
||||
t_CNNType GetTypeDigital();
|
||||
t_CNNType GetTypeAnalog();
|
||||
|
||||
int CleanTempFolder();
|
||||
|
||||
string name(){return "ClassFlowControll";};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
53
code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h
Normal file
53
code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef __CLASSFLOWIMAGE_CLASS__
|
||||
#define __CLASSFLOWIMAGE_CLASS__
|
||||
|
||||
#include "ClassFlowImage.h"
|
||||
|
||||
struct roi {
|
||||
int posx, posy, deltax, deltay;
|
||||
float result_float;
|
||||
int result_klasse;
|
||||
string name;
|
||||
CImageBasis *image, *image_org;
|
||||
};
|
||||
|
||||
struct general {
|
||||
string name;
|
||||
std::vector<roi*> ROI;
|
||||
};
|
||||
|
||||
|
||||
struct NumberPost {
|
||||
float MaxRateValue;
|
||||
bool useMaxRateValue;
|
||||
bool ErrorMessage;
|
||||
bool PreValueOkay;
|
||||
bool AllowNegativeRates;
|
||||
bool checkDigitIncreaseConsistency;
|
||||
time_t lastvalue;
|
||||
string timeStamp;
|
||||
float FlowRateAct; // m3 / min
|
||||
float PreValue; // letzter Wert, der gut ausgelesen wurde
|
||||
float Value; // letzer ausgelesener Wert, inkl. Korrekturen
|
||||
string ReturnRateValue; // RückgabewertRate
|
||||
string ReturnRawValue; // Rohwert (mit N & führenden 0)
|
||||
string ReturnValue; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
|
||||
string ReturnPreValue; // korrigierter Rückgabewert ohne Fehlermeldung
|
||||
string ReturnValueNoError;
|
||||
string ErrorMessageText; // Fehlermeldung bei Consistency Check
|
||||
int AnzahlAnalog;
|
||||
int AnzahlDigital;
|
||||
int DecimalShift;
|
||||
int DecimalShiftInitial;
|
||||
int Nachkomma;
|
||||
|
||||
bool isExtendedResolution;
|
||||
|
||||
general *digit_roi;
|
||||
general *analog_roi;
|
||||
|
||||
string name;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,420 +0,0 @@
|
||||
#include "ClassFlowDigit.h"
|
||||
|
||||
|
||||
//#include "CFindTemplate.h"
|
||||
//#include "CTfLiteClass.h"
|
||||
|
||||
// #define OHNETFLITE
|
||||
|
||||
#ifndef OHNETFLITE
|
||||
#include "CTfLiteClass.h"
|
||||
#endif
|
||||
|
||||
// #include "bitmap_image.hpp"
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
static const char* TAG = "flow_digital";
|
||||
|
||||
|
||||
void ClassFlowDigit::SetInitialParameter(void)
|
||||
{
|
||||
string cnnmodelfile = "";
|
||||
modelxsize = 1;
|
||||
modelysize = 1;
|
||||
ListFlowControll = NULL;
|
||||
previousElement = NULL;
|
||||
SaveAllFiles = false;
|
||||
disabled = false;
|
||||
DecimalShift = 0;
|
||||
DecimalShiftEnabled = false;
|
||||
}
|
||||
|
||||
ClassFlowDigit::ClassFlowDigit() : ClassFlowImage(TAG)
|
||||
{
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlowDigit::ClassFlowDigit(std::vector<ClassFlow*>* lfc) : ClassFlowImage(lfc, TAG)
|
||||
{
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
||||
{
|
||||
flowpostalignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClassFlowDigit::ClassFlowDigit(std::vector<ClassFlow*>* lfc, ClassFlow *_prev) : ClassFlowImage(lfc, _prev, TAG)
|
||||
{
|
||||
SetInitialParameter();
|
||||
ListFlowControll = lfc;
|
||||
previousElement = _prev;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAlignment") == 0)
|
||||
{
|
||||
flowpostalignment = (ClassFlowAlignment*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string ClassFlowDigit::getReadout(int _digit = 0)
|
||||
{
|
||||
string rst = "";
|
||||
|
||||
for (int i = 0; i < DIGIT[_digit]->ROI.size(); ++i)
|
||||
{
|
||||
if (DIGIT[_digit]->ROI[i]->resultklasse == 10)
|
||||
rst = rst + "N";
|
||||
else
|
||||
rst = rst + std::to_string(DIGIT[_digit]->ROI[i]->resultklasse);
|
||||
}
|
||||
|
||||
return rst;
|
||||
}
|
||||
|
||||
bool ClassFlowDigit::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
std::vector<string> zerlegt;
|
||||
|
||||
aktparamgraph = trim(aktparamgraph);
|
||||
|
||||
if (aktparamgraph.size() == 0)
|
||||
if (!this->GetNextParagraph(pfile, aktparamgraph))
|
||||
return false;
|
||||
|
||||
printf("aktparamgraph: %s\n", aktparamgraph.c_str());
|
||||
|
||||
if ((aktparamgraph.compare(0, 7, "[Digits") != 0) && (aktparamgraph.compare(0, 8, ";[Digits") != 0)) // Paragraph passt nich zu MakeImage
|
||||
return false;
|
||||
|
||||
int _pospkt = aktparamgraph.find_first_of(".");
|
||||
int _posklammerzu = aktparamgraph.find_first_of("]");
|
||||
if (_pospkt > -1)
|
||||
NameDigit = aktparamgraph.substr(_pospkt+1, _posklammerzu - _pospkt-1);
|
||||
else
|
||||
NameDigit = "";
|
||||
printf("Name Digit: %s\n", NameDigit.c_str());
|
||||
|
||||
if (aktparamgraph[0] == ';')
|
||||
{
|
||||
disabled = true;
|
||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph));
|
||||
printf("[Digits] is disabled !!!\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
while (getNextLine(pfile, &aktparamgraph) && !isNewParagraph(aktparamgraph))
|
||||
{
|
||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
||||
{
|
||||
LogImageLocation = "/sdcard" + zerlegt[1];
|
||||
isLogImage = true;
|
||||
}
|
||||
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
||||
{
|
||||
cnnmodelfile = zerlegt[1];
|
||||
}
|
||||
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
||||
{
|
||||
modelxsize = std::stoi(zerlegt[1]);
|
||||
modelysize = std::stoi(zerlegt[2]);
|
||||
}
|
||||
if (zerlegt.size() >= 5)
|
||||
{
|
||||
digit* _digit = GetDIGIT(zerlegt[0], true);
|
||||
roi* neuroi = _digit->ROI[_digit->ROI.size()-1];
|
||||
neuroi->posx = std::stoi(zerlegt[1]);
|
||||
neuroi->posy = std::stoi(zerlegt[2]);
|
||||
neuroi->deltax = std::stoi(zerlegt[3]);
|
||||
neuroi->deltay = std::stoi(zerlegt[4]);
|
||||
neuroi->resultklasse = -1;
|
||||
neuroi->image = NULL;
|
||||
neuroi->image_org = NULL;
|
||||
}
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "SAVEALLFILES") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
SaveAllFiles = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
|
||||
for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
|
||||
{
|
||||
DIGIT[_dig]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
|
||||
DIGIT[_dig]->ROI[i]->image_org = new CImageBasis(DIGIT[_dig]->ROI[i]->deltax, DIGIT[_dig]->ROI[i]->deltay, 3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
digit* ClassFlowDigit::FindDIGIT(string _name_number)
|
||||
{
|
||||
for (int i = 0; i < DIGIT.size(); ++i)
|
||||
{
|
||||
if (DIGIT[i]->name == _name_number)
|
||||
return DIGIT[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
digit* ClassFlowDigit::GetDIGIT(string _name, bool _create = true)
|
||||
{
|
||||
string _digit, _roi;
|
||||
int _pospunkt = _name.find_first_of(".");
|
||||
// printf("Name: %s, Pospunkt: %d\n", _name.c_str(), _pospunkt);
|
||||
if (_pospunkt > -1)
|
||||
{
|
||||
_digit = _name.substr(0, _pospunkt);
|
||||
_roi = _name.substr(_pospunkt+1, _name.length() - _pospunkt - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
_digit = "default";
|
||||
_roi = _name;
|
||||
}
|
||||
|
||||
digit *_ret = NULL;
|
||||
|
||||
for (int i = 0; i < DIGIT.size(); ++i)
|
||||
{
|
||||
if (DIGIT[i]->name == _digit)
|
||||
_ret = DIGIT[i];
|
||||
}
|
||||
|
||||
if (!_create) // nicht gefunden und soll auch nicht erzeugt werden, ggf. geht eine NULL zurück
|
||||
return _ret;
|
||||
|
||||
if (_ret == NULL)
|
||||
{
|
||||
_ret = new digit;
|
||||
_ret->name = _digit;
|
||||
DIGIT.push_back(_ret);
|
||||
}
|
||||
|
||||
roi* neuroi = new roi;
|
||||
neuroi->name = _roi;
|
||||
_ret->ROI.push_back(neuroi);
|
||||
|
||||
printf("GetDIGIT - digit %s - roi %s\n", _digit.c_str(), _roi.c_str());
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
string ClassFlowDigit::getHTMLSingleStep(string host)
|
||||
{
|
||||
string result, zw;
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
|
||||
result = "<p>Found ROIs: </p> <p><img src=\"" + host + "/img_tmp/alg_roi.jpg\"></p>\n";
|
||||
result = result + "Digital Counter: <p> ";
|
||||
|
||||
htmlinfo = GetHTMLInfo();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (htmlinfo[i]->val == 10)
|
||||
zw = "NaN";
|
||||
else
|
||||
{
|
||||
zw = to_string((int) htmlinfo[i]->val);
|
||||
}
|
||||
result = result + "<img src=\"" + host + "/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
||||
delete htmlinfo[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowDigit::doFlow(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
if (!doAlignAndCut(time)){
|
||||
return false;
|
||||
};
|
||||
|
||||
doNeuralNetwork(time);
|
||||
|
||||
RemoveOldLogs();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassFlowDigit::doAlignAndCut(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
CAlignAndCutImage *caic = flowpostalignment->GetAlignAndCutImage();
|
||||
|
||||
for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
|
||||
{
|
||||
printf("DIGIT[_dig]->ROI.size() %d\n", DIGIT[_dig]->ROI.size());
|
||||
for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
|
||||
{
|
||||
printf("DigitalDigit %d - Align&Cut\n", i);
|
||||
|
||||
caic->CutAndSave(DIGIT[_dig]->ROI[i]->posx, DIGIT[_dig]->ROI[i]->posy, DIGIT[_dig]->ROI[i]->deltax, DIGIT[_dig]->ROI[i]->deltay, DIGIT[_dig]->ROI[i]->image_org);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (DIGIT[_dig]->name == "default")
|
||||
DIGIT[_dig]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->ROI[i]->name + ".jpg"));
|
||||
else
|
||||
DIGIT[_dig]->ROI[i]->image_org->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".jpg"));
|
||||
}
|
||||
|
||||
DIGIT[_dig]->ROI[i]->image_org->Resize(modelxsize, modelysize, DIGIT[_dig]->ROI[i]->image);
|
||||
if (SaveAllFiles)
|
||||
{
|
||||
if (DIGIT[_dig]->name == "default")
|
||||
DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
|
||||
else
|
||||
DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassFlowDigit::doNeuralNetwork(string time)
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
string logPath = CreateLogFolder(time);
|
||||
|
||||
#ifndef OHNETFLITE
|
||||
CTfLiteClass *tflite = new CTfLiteClass;
|
||||
string zwcnn = FormatFileName("/sdcard" + cnnmodelfile);
|
||||
printf(zwcnn.c_str());printf("\n");
|
||||
if (!tflite->LoadModel(zwcnn)) {
|
||||
printf("Can't read model file /sdcard%s\n", cnnmodelfile.c_str());
|
||||
delete tflite;
|
||||
return false;
|
||||
}
|
||||
|
||||
tflite->MakeAllocate();
|
||||
#endif
|
||||
|
||||
for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
|
||||
for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
|
||||
{
|
||||
printf("DigitalDigit %d - TfLite\n", i);
|
||||
|
||||
DIGIT[_dig]->ROI[i]->resultklasse = 0;
|
||||
#ifndef OHNETFLITE
|
||||
DIGIT[_dig]->ROI[i]->resultklasse = tflite->GetClassFromImageBasis(DIGIT[_dig]->ROI[i]->image);
|
||||
|
||||
#endif
|
||||
printf("Result Digit%i: %d\n", i, DIGIT[_dig]->ROI[i]->resultklasse);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
LogImage(logPath, DIGIT[_dig]->ROI[i]->name, NULL, &DIGIT[_dig]->ROI[i]->resultklasse, time, DIGIT[_dig]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
#ifndef OHNETFLITE
|
||||
delete tflite;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClassFlowDigit::DrawROI(CImageBasis *_zw)
|
||||
{
|
||||
for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
|
||||
for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
|
||||
_zw->drawRect(DIGIT[_dig]->ROI[i]->posx, DIGIT[_dig]->ROI[i]->posy, DIGIT[_dig]->ROI[i]->deltax, DIGIT[_dig]->ROI[i]->deltay, 0, 0, (255 - _dig*100), 2);
|
||||
}
|
||||
|
||||
std::vector<HTMLInfo*> ClassFlowDigit::GetHTMLInfo()
|
||||
{
|
||||
std::vector<HTMLInfo*> result;
|
||||
|
||||
for (int _dig = 0; _dig < DIGIT.size(); ++_dig)
|
||||
for (int i = 0; i < DIGIT[_dig]->ROI.size(); ++i)
|
||||
{
|
||||
if (DIGIT[_dig]->name == "default")
|
||||
DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
|
||||
else
|
||||
DIGIT[_dig]->ROI[i]->image->SaveToFile(FormatFileName("/sdcard/img_tmp/" + DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".bmp"));
|
||||
|
||||
|
||||
HTMLInfo *zw = new HTMLInfo;
|
||||
if (DIGIT[_dig]->name == "default")
|
||||
{
|
||||
zw->filename = DIGIT[_dig]->ROI[i]->name + ".bmp";
|
||||
zw->filename_org = DIGIT[_dig]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
else
|
||||
{
|
||||
zw->filename = DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".bmp";
|
||||
zw->filename_org = DIGIT[_dig]->name + "_" + DIGIT[_dig]->ROI[i]->name + ".jpg";
|
||||
}
|
||||
|
||||
zw->val = DIGIT[_dig]->ROI[i]->resultklasse;
|
||||
zw->image = DIGIT[_dig]->ROI[i]->image;
|
||||
zw->image_org = DIGIT[_dig]->ROI[i]->image_org;
|
||||
result.push_back(zw);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int ClassFlowDigit::getAnzahlDIGIT()
|
||||
{
|
||||
return DIGIT.size();
|
||||
}
|
||||
|
||||
string ClassFlowDigit::getNameDIGIT(int _digit)
|
||||
{
|
||||
if (_digit < DIGIT.size())
|
||||
return DIGIT[_digit]->name;
|
||||
|
||||
return "DIGIT DOES NOT EXIST";
|
||||
}
|
||||
|
||||
digit* ClassFlowDigit::GetDIGIT(int _digit)
|
||||
{
|
||||
if (_digit < DIGIT.size())
|
||||
return DIGIT[_digit];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ClassFlowDigit::UpdateNameNumbers(std::vector<std::string> *_name_numbers)
|
||||
{
|
||||
for (int _dig = 0; _dig < DIGIT.size(); _dig++)
|
||||
{
|
||||
std::string _name = DIGIT[_dig]->name;
|
||||
bool found = false;
|
||||
for (int i = 0; i < (*_name_numbers).size(); ++i)
|
||||
{
|
||||
if ((*_name_numbers)[i] == _name)
|
||||
found = true;
|
||||
}
|
||||
if (!found)
|
||||
(*_name_numbers).push_back(_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
#pragma once
|
||||
#include "ClassFlowImage.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
#include "Helper.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
|
||||
struct roi {
|
||||
int posx, posy, deltax, deltay;
|
||||
int resultklasse;
|
||||
string name;
|
||||
CImageBasis *image, *image_org;
|
||||
roi* next;
|
||||
};
|
||||
|
||||
struct digit {
|
||||
string name;
|
||||
std::vector<roi*> ROI;
|
||||
};
|
||||
|
||||
class ClassFlowDigit :
|
||||
public ClassFlowImage
|
||||
{
|
||||
protected:
|
||||
// std::vector<roi*> ROI;
|
||||
std::vector<digit*> DIGIT;
|
||||
string cnnmodelfile;
|
||||
int modelxsize, modelysize;
|
||||
bool SaveAllFiles;
|
||||
string NameDigit;
|
||||
int DecimalShift;
|
||||
bool DecimalShiftEnabled;
|
||||
|
||||
|
||||
ClassFlowAlignment* flowpostalignment;
|
||||
|
||||
bool doNeuralNetwork(string time);
|
||||
bool doAlignAndCut(string time);
|
||||
|
||||
|
||||
void SetInitialParameter(void);
|
||||
|
||||
public:
|
||||
ClassFlowDigit();
|
||||
ClassFlowDigit(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowDigit(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getHTMLSingleStep(string host);
|
||||
string getReadout(int _digit);
|
||||
std::vector<HTMLInfo*> GetHTMLInfo();
|
||||
|
||||
int getAnzahlDIGIT();
|
||||
digit* GetDIGIT(int _digit);
|
||||
digit* GetDIGIT(string _name, bool _create);
|
||||
digit* FindDIGIT(string _name_number);
|
||||
|
||||
string getNameDIGIT(int _digit);
|
||||
|
||||
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
|
||||
|
||||
void DrawROI(CImageBasis *_zw);
|
||||
|
||||
string name(){return "ClassFlowDigit";};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,15 @@
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "time_sntp.h"
|
||||
#include "ClassLogFile.h"
|
||||
#include "CImageBasis.h"
|
||||
@@ -48,9 +56,14 @@ void ClassFlowImage::LogImage(string logPath, string name, float *resultFloat, i
|
||||
if (!isLogImage)
|
||||
return;
|
||||
|
||||
|
||||
char buf[10];
|
||||
|
||||
if (resultFloat != NULL) {
|
||||
sprintf(buf, "%.1f_", *resultFloat);
|
||||
if (*resultFloat < 0)
|
||||
sprintf(buf, "N.N_");
|
||||
else
|
||||
sprintf(buf, "%.1f_", *resultFloat);
|
||||
} else if (resultInt != NULL) {
|
||||
sprintf(buf, "%d_", *resultInt);
|
||||
} else {
|
||||
|
||||
@@ -122,6 +122,12 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowMQTT::GetMQTTMainTopic()
|
||||
{
|
||||
return maintopic;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
{
|
||||
if (!MQTTenable)
|
||||
@@ -129,6 +135,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
|
||||
std::string result;
|
||||
std::string resulterror = "";
|
||||
std::string resultraw = "";
|
||||
std::string resultrate = "";
|
||||
std::string resulttimestamp = "";
|
||||
string zw = "";
|
||||
@@ -148,37 +155,56 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
std::vector<NumberPost*> NUMBERS = flowpostprocessing->GetNumbers();
|
||||
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||
|
||||
for (int i = 0; i < NUMBERS.size(); ++i)
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
result = NUMBERS[i]->ReturnValueNoError;
|
||||
resulterror = NUMBERS[i]->ErrorMessageText;
|
||||
resultrate = std::to_string(NUMBERS[i]->FlowRateAct);
|
||||
resulttimestamp = NUMBERS[i]->timeStamp;
|
||||
result = (*NUMBERS)[i]->ReturnValueNoError;
|
||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
|
||||
namenumber = NUMBERS[i]->name;
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
if (namenumber == "default")
|
||||
namenumber = maintopic + "/";
|
||||
else
|
||||
namenumber = maintopic + "/" + namenumber + "/";
|
||||
|
||||
zw = namenumber + "value";
|
||||
MQTTPublish(zw, result);
|
||||
zw = namenumber + "value";
|
||||
if (result.length() > 0)
|
||||
MQTTPublish(zw, result);
|
||||
|
||||
zw = namenumber + "error";
|
||||
MQTTPublish(zw, resulterror, 1);
|
||||
zw = namenumber + "error";
|
||||
if (resulterror.length() > 0)
|
||||
MQTTPublish(zw, resulterror, 1);
|
||||
|
||||
zw = namenumber + "rate";
|
||||
MQTTPublish(zw, resultrate);
|
||||
zw = namenumber + "rate";
|
||||
if (resultrate.length() > 0)
|
||||
MQTTPublish(zw, resultrate);
|
||||
|
||||
zw = namenumber + "raw";
|
||||
if (resultraw.length() > 0)
|
||||
MQTTPublish(zw, resultraw);
|
||||
|
||||
zw = namenumber + "timestamp";
|
||||
MQTTPublish(zw, resulttimestamp);
|
||||
if (resulttimestamp.length() > 0)
|
||||
MQTTPublish(zw, resulttimestamp);
|
||||
|
||||
|
||||
std::string json="{\"value\":"+result;
|
||||
json += ",\"error\":\""+resulterror;
|
||||
json += "\",\"rate\":"+resultrate;
|
||||
std::string json = "";
|
||||
|
||||
if (result.length() > 0)
|
||||
json += "{\"value\":"+result;
|
||||
else
|
||||
json += "{\"value\":\"\"";
|
||||
|
||||
json += ",\"raw\":\""+resultraw;
|
||||
json += "\",\"error\":\""+resulterror;
|
||||
if (resultrate.length() > 0)
|
||||
json += "\",\"rate\":"+resultrate;
|
||||
else
|
||||
json += "\",\"rate\":\"\"";
|
||||
json += ",\"timestamp\":\""+resulttimestamp+"\"}";
|
||||
|
||||
zw = namenumber + "json";
|
||||
|
||||
@@ -23,6 +23,8 @@ public:
|
||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowMQTT(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
|
||||
string GetMQTTMainTopic();
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string name(){return "ClassFlowMQTT";};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
|
||||
#include "Helper.h"
|
||||
#include "ClassFlowMakeImage.h"
|
||||
#include "ClassLogFile.h"
|
||||
@@ -28,21 +27,27 @@ string ClassFlowPostProcessing::GetPreValue(std::string _number)
|
||||
if (NUMBERS[i]->name == _number)
|
||||
index = i;
|
||||
|
||||
// result = RundeOutput(NUMBERS[index]->PreValue, -NUMBERS[index]->DecimalShift);
|
||||
result = RundeOutput(NUMBERS[index]->PreValue, NUMBERS[index]->Nachkomma);
|
||||
|
||||
// if (NUMBERS[index]->digit_roi && NUMBERS[index]->analog_roi)
|
||||
// result = RundeOutput(NUMBERS[index]->PreValue, NUMBERS[index]->AnzahlAnalog - NUMBERS[index]->DecimalShift);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ClassFlowPostProcessing::SetPreValue(float zw, string _numbers)
|
||||
void ClassFlowPostProcessing::SetPreValue(float zw, string _numbers, bool _extern)
|
||||
{
|
||||
printf("SetPrevalue: %f, %s\n", zw, _numbers.c_str());
|
||||
for (int j = 0; j < NUMBERS.size(); ++j)
|
||||
{
|
||||
// printf("Number %d, %s\n", j, NUMBERS[j]->name.c_str());
|
||||
if (NUMBERS[j]->name == _numbers)
|
||||
{
|
||||
NUMBERS[j]->PreValue = zw;
|
||||
if (_extern)
|
||||
{
|
||||
time(&(NUMBERS[j]->lastvalue));
|
||||
localtime(&(NUMBERS[j]->lastvalue));
|
||||
}
|
||||
// printf("Found %d! - set to %f\n", j, NUMBERS[j]->PreValue);
|
||||
}
|
||||
}
|
||||
UpdatePreValueINI = true;
|
||||
SavePreValue();
|
||||
@@ -84,7 +89,7 @@ bool ClassFlowPostProcessing::LoadPreValue(void)
|
||||
if (NUMBERS[j]->name == name)
|
||||
{
|
||||
NUMBERS[j]->PreValue = stof(zwvalue.c_str());
|
||||
NUMBERS[j]->ReturnPreValue = RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
|
||||
NUMBERS[j]->ReturnPreValue = RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma + 1); // SIcherheitshalber 1 Stelle mehr, da ggf. Exgtended Resolution an ist (wird erst beim ersten Durchlauf gesetzt)
|
||||
|
||||
time_t tStart;
|
||||
int yy, month, dd, hh, mm, ss;
|
||||
@@ -112,15 +117,17 @@ bool ClassFlowPostProcessing::LoadPreValue(void)
|
||||
else
|
||||
{
|
||||
NUMBERS[j]->PreValueOkay = true;
|
||||
/*
|
||||
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
|
||||
NUMBERS[j]->ReturnValue = to_string(NUMBERS[j]->Value);
|
||||
NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnValue;
|
||||
|
||||
if (NUMBERS[j]->digit_roi || NUMBERS[j]->analog_roi)
|
||||
{
|
||||
NUMBERS[j]->ReturnValue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift);
|
||||
NUMBERS[j]->ReturnValue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma + 1); // SIcherheitshalber 1 Stelle mehr, da ggf. Exgtended Resolution an ist (wird erst beim ersten Durchlauf gesetzt)
|
||||
NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnValue;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
@@ -180,7 +187,7 @@ bool ClassFlowPostProcessing::LoadPreValue(void)
|
||||
|
||||
if (NUMBERS[0]->digit_roi || NUMBERS[0]->analog_roi)
|
||||
{
|
||||
NUMBERS[0]->ReturnValue = RundeOutput(NUMBERS[0]->Value, NUMBERS[0]->AnzahlAnalog - NUMBERS[0]->DecimalShift);
|
||||
NUMBERS[0]->ReturnValue = RundeOutput(NUMBERS[0]->Value, NUMBERS[0]->Nachkomma);
|
||||
NUMBERS[0]->ReturnValueNoError = NUMBERS[0]->ReturnValue;
|
||||
}
|
||||
|
||||
@@ -207,8 +214,9 @@ void ClassFlowPostProcessing::SavePreValue()
|
||||
struct tm* timeinfo = localtime(&NUMBERS[j]->lastvalue);
|
||||
strftime(buffer, 80, PREVALUE_TIME_FORMAT_OUTPUT, timeinfo);
|
||||
NUMBERS[j]->timeStamp = std::string(buffer);
|
||||
// printf("SaverPreValue %d, Value: %f, Nachkomma %d\n", j, NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
|
||||
|
||||
_zw = NUMBERS[j]->name + "\t" + NUMBERS[j]->timeStamp + "\t" + RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma) + "\n";
|
||||
_zw = NUMBERS[j]->name + "\t" + NUMBERS[j]->timeStamp + "\t" + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + "\n";
|
||||
printf("Write PreValue Zeile: %s\n", _zw.c_str());
|
||||
|
||||
fputs(_zw.c_str(), pFile);
|
||||
@@ -220,21 +228,19 @@ void ClassFlowPostProcessing::SavePreValue()
|
||||
}
|
||||
|
||||
|
||||
ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc)
|
||||
ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit)
|
||||
{
|
||||
// FlowRateAct = 0;
|
||||
PreValueUse = false;
|
||||
PreValueAgeStartup = 30;
|
||||
ErrorMessage = false;
|
||||
ListFlowControll = NULL;
|
||||
// PreValueOkay = false;
|
||||
// DecimalShift = 0;
|
||||
// ErrorMessageText = "";
|
||||
// timeStamp = "";
|
||||
FilePreValue = FormatFileName("/sdcard/config/prevalue.ini");
|
||||
ListFlowControll = lfc;
|
||||
flowMakeImage = NULL;
|
||||
UpdatePreValueINI = false;
|
||||
IgnoreLeadingNaN = false;
|
||||
flowAnalog = _analog;
|
||||
flowDigit = _digit;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
@@ -245,6 +251,36 @@ ClassFlowPostProcessing::ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc)
|
||||
}
|
||||
}
|
||||
|
||||
void ClassFlowPostProcessing::handleDecimalExtendedResolution(string _decsep, string _value)
|
||||
{
|
||||
string _digit, _decpos;
|
||||
int _pospunkt = _decsep.find_first_of(".");
|
||||
// printf("Name: %s, Pospunkt: %d\n", _decsep.c_str(), _pospunkt);
|
||||
if (_pospunkt > -1)
|
||||
_digit = _decsep.substr(0, _pospunkt);
|
||||
else
|
||||
_digit = "default";
|
||||
|
||||
for (int j = 0; j < NUMBERS.size(); ++j)
|
||||
{
|
||||
bool _zwdc = false;
|
||||
|
||||
if (toUpper(_value) == "TRUE")
|
||||
_zwdc = true;
|
||||
|
||||
if (_digit == "default") // erstmal auf default setzen (falls sonst nichts gesetzt)
|
||||
{
|
||||
NUMBERS[j]->isExtendedResolution = _zwdc;
|
||||
}
|
||||
|
||||
if (NUMBERS[j]->name == _digit)
|
||||
{
|
||||
NUMBERS[j]->isExtendedResolution = _zwdc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowPostProcessing::handleDecimalSeparator(string _decsep, string _value)
|
||||
{
|
||||
string _digit, _decpos;
|
||||
@@ -259,20 +295,26 @@ void ClassFlowPostProcessing::handleDecimalSeparator(string _decsep, string _val
|
||||
{
|
||||
int _zwdc = 0;
|
||||
|
||||
try
|
||||
// try
|
||||
{
|
||||
_zwdc = stoi(_value);
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
/* catch(const std::exception& e)
|
||||
{
|
||||
printf("ERROR - Decimalshift is not a number: %s\n", _value.c_str());
|
||||
}
|
||||
|
||||
*/
|
||||
if (_digit == "default") // erstmal auf default setzen (falls sonst nichts gesetzt)
|
||||
{
|
||||
NUMBERS[j]->DecimalShift = _zwdc;
|
||||
NUMBERS[j]->DecimalShiftInitial = _zwdc;
|
||||
}
|
||||
|
||||
if (NUMBERS[j]->name == _digit)
|
||||
{
|
||||
NUMBERS[j]->DecimalShift = _zwdc;
|
||||
NUMBERS[j]->DecimalShiftInitial = _zwdc;
|
||||
}
|
||||
|
||||
NUMBERS[j]->Nachkomma = NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift;
|
||||
}
|
||||
@@ -292,15 +334,15 @@ void ClassFlowPostProcessing::handleMaxRateValue(string _decsep, string _value)
|
||||
{
|
||||
float _zwdc = 1;
|
||||
|
||||
try
|
||||
// try
|
||||
{
|
||||
_zwdc = stof(_value);
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
/* catch(const std::exception& e)
|
||||
{
|
||||
printf("ERROR - MaxRateValue is not a number: %s\n", _value.c_str());
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
if (_digit == "default") // erstmal auf default setzen (falls sonst nichts gesetzt)
|
||||
{
|
||||
@@ -340,6 +382,11 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||
std::string _param = GetParameterName(zerlegt[0]);
|
||||
|
||||
if ((toUpper(_param) == "EXTENDEDRESOLUTION") && (zerlegt.size() > 1))
|
||||
{
|
||||
handleDecimalExtendedResolution(zerlegt[0], zerlegt[1]);
|
||||
}
|
||||
|
||||
if ((toUpper(_param) == "DECIMALSHIFT") && (zerlegt.size() > 1))
|
||||
{
|
||||
handleDecimalSeparator(zerlegt[0], zerlegt[1]);
|
||||
@@ -373,6 +420,13 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
ErrorMessage = true;
|
||||
}
|
||||
if ((toUpper(_param) == "IGNORELEADINGNAN") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
IgnoreLeadingNaN = true;
|
||||
}
|
||||
|
||||
|
||||
if ((toUpper(_param) == "PREVALUEAGESTARTUP") && (zerlegt.size() > 1))
|
||||
{
|
||||
PreValueAgeStartup = std::stoi(zerlegt[1]);
|
||||
@@ -388,33 +442,20 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
|
||||
void ClassFlowPostProcessing::InitNUMBERS()
|
||||
{
|
||||
// ClassFlowDigit* _cdigit = NULL;
|
||||
// ClassFlowAnalog* _canalog = NULL;
|
||||
int anzDIGIT = 0;
|
||||
int anzANALOG = 0;
|
||||
std::vector<std::string> name_numbers;
|
||||
|
||||
flowAnalog = NULL;
|
||||
flowDigit = NULL;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowDigit") == 0)
|
||||
{
|
||||
flowDigit = (ClassFlowDigit*) (*ListFlowControll)[i];
|
||||
anzDIGIT = flowDigit->getAnzahlDIGIT();
|
||||
}
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowAnalog") == 0)
|
||||
{
|
||||
flowAnalog = (ClassFlowAnalog*)(*ListFlowControll)[i];
|
||||
anzANALOG = flowAnalog->getAnzahlANALOG();
|
||||
}
|
||||
}
|
||||
|
||||
if (flowDigit)
|
||||
{
|
||||
anzDIGIT = flowDigit->getAnzahlGENERAL();
|
||||
flowDigit->UpdateNameNumbers(&name_numbers);
|
||||
}
|
||||
if (flowAnalog)
|
||||
{
|
||||
anzANALOG = flowAnalog->getAnzahlGENERAL();
|
||||
flowAnalog->UpdateNameNumbers(&name_numbers);
|
||||
}
|
||||
|
||||
printf("Anzahl NUMBERS: %d - DIGITS: %d, ANALOG: %d\n", name_numbers.size(), anzDIGIT, anzANALOG);
|
||||
|
||||
@@ -426,7 +467,7 @@ void ClassFlowPostProcessing::InitNUMBERS()
|
||||
|
||||
_number->digit_roi = NULL;
|
||||
if (flowDigit)
|
||||
_number->digit_roi = flowDigit->FindDIGIT(name_numbers[_num]);
|
||||
_number->digit_roi = flowDigit->FindGENERAL(name_numbers[_num]);
|
||||
|
||||
if (_number->digit_roi)
|
||||
_number->AnzahlDigital = _number->digit_roi->ROI.size();
|
||||
@@ -435,7 +476,7 @@ void ClassFlowPostProcessing::InitNUMBERS()
|
||||
|
||||
_number->analog_roi = NULL;
|
||||
if (flowAnalog)
|
||||
_number->analog_roi = flowAnalog->FindANALOG(name_numbers[_num]);
|
||||
_number->analog_roi = flowAnalog->FindGENERAL(name_numbers[_num]);
|
||||
|
||||
|
||||
if (_number->analog_roi)
|
||||
@@ -453,9 +494,10 @@ void ClassFlowPostProcessing::InitNUMBERS()
|
||||
_number->MaxRateValue = 0.1;
|
||||
_number->useMaxRateValue = false;
|
||||
_number->checkDigitIncreaseConsistency = false;
|
||||
_number->PreValueOkay = false;
|
||||
_number->useMaxRateValue = false;
|
||||
_number->DecimalShift = 0;
|
||||
_number->DecimalShiftInitial = 0;
|
||||
_number->isExtendedResolution = false;
|
||||
|
||||
|
||||
_number->FlowRateAct = 0; // m3 / min
|
||||
_number->PreValue = 0; // letzter Wert, der gut ausgelesen wurde
|
||||
@@ -526,7 +568,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||
time_t imagetime = 0;
|
||||
string rohwert;
|
||||
|
||||
// ErrorMessageText = "";
|
||||
// Update Nachkomma, da sich beim Wechsel von CNNType Auto --> xyz auch die Nachkommastellen ändern können:
|
||||
|
||||
imagetime = flowMakeImage->getTimeImageTaken();
|
||||
if (imagetime == 0)
|
||||
@@ -545,14 +587,31 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||
NUMBERS[j]->ReturnRawValue = "";
|
||||
NUMBERS[j]->ErrorMessageText = "";
|
||||
|
||||
UpdateNachkommaDecimalShift();
|
||||
|
||||
if (NUMBERS[j]->digit_roi)
|
||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j);
|
||||
{
|
||||
if (NUMBERS[j]->analog_roi)
|
||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false);
|
||||
else
|
||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution); // Extended Resolution nur falls es keine analogen Ziffern gibt
|
||||
}
|
||||
if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi)
|
||||
NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + ".";
|
||||
if (NUMBERS[j]->analog_roi)
|
||||
NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + flowAnalog->getReadout(j);
|
||||
|
||||
NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
|
||||
if (NUMBERS[j]->analog_roi)
|
||||
NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution);
|
||||
|
||||
NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
|
||||
|
||||
|
||||
if (IgnoreLeadingNaN)
|
||||
{
|
||||
while ((NUMBERS[j]->ReturnRawValue.length() > 1) && (NUMBERS[j]->ReturnRawValue[0] == 'N'))
|
||||
{
|
||||
NUMBERS[j]->ReturnRawValue.erase(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
rohwert = NUMBERS[j]->ReturnRawValue;
|
||||
|
||||
@@ -590,16 +649,21 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||
NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue);
|
||||
}
|
||||
|
||||
zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift);
|
||||
zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma);
|
||||
|
||||
if ((!NUMBERS[j]->AllowNegativeRates) && (NUMBERS[j]->Value < NUMBERS[j]->PreValue))
|
||||
{
|
||||
NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Neg. Rate - Read: " + zwvalue + " - Raw: " + NUMBERS[j]->ReturnRawValue + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " ";
|
||||
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
|
||||
zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->AnzahlAnalog - NUMBERS[j]->DecimalShift);
|
||||
zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma);
|
||||
}
|
||||
|
||||
if (NUMBERS[j]->useMaxRateValue && (abs(NUMBERS[j]->Value - NUMBERS[j]->PreValue) > NUMBERS[j]->MaxRateValue))
|
||||
double difference = difftime(imagetime, NUMBERS[j]->lastvalue); // in Sekunden
|
||||
difference /= 60; // in Minuten
|
||||
NUMBERS[j]->FlowRateAct = (NUMBERS[j]->Value - NUMBERS[j]->PreValue) / difference;
|
||||
NUMBERS[j]->ReturnRateValue = std::to_string(NUMBERS[j]->FlowRateAct);
|
||||
|
||||
if (NUMBERS[j]->useMaxRateValue && (abs(NUMBERS[j]->FlowRateAct) > NUMBERS[j]->MaxRateValue))
|
||||
{
|
||||
NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Rate too high - Read: " + RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma) + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
|
||||
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
|
||||
@@ -611,26 +675,76 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||
if (NUMBERS[j]->ErrorMessage && (NUMBERS[j]->ErrorMessageText.length() > 0))
|
||||
NUMBERS[j]->ReturnValue = NUMBERS[j]->ReturnValue + "\t" + NUMBERS[j]->ErrorMessageText;
|
||||
|
||||
|
||||
double difference = difftime(imagetime, NUMBERS[j]->lastvalue); // in Sekunden
|
||||
difference /= 60; // in Minuten
|
||||
NUMBERS[j]->FlowRateAct = (NUMBERS[j]->Value - NUMBERS[j]->PreValue) / difference;
|
||||
NUMBERS[j]->lastvalue = imagetime;
|
||||
|
||||
if (NUMBERS[j]->ErrorMessageText.length() == 0)
|
||||
{
|
||||
NUMBERS[j]->lastvalue = imagetime;
|
||||
NUMBERS[j]->PreValue = NUMBERS[j]->Value;
|
||||
|
||||
NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnValue;
|
||||
NUMBERS[j]->ReturnPreValue = RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
|
||||
NUMBERS[j]->ErrorMessageText = "no error";
|
||||
UpdatePreValueINI = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
NUMBERS[j]->ReturnRateValue = "";
|
||||
NUMBERS[j]->ReturnValue = "";
|
||||
NUMBERS[j]->ReturnValueNoError = "";
|
||||
NUMBERS[j]->timeStamp = "";
|
||||
|
||||
}
|
||||
}
|
||||
string _zw = "PostProcessing - Raw: " + NUMBERS[j]->ReturnRawValue + " Value: " + NUMBERS[j]->ReturnValue + " Error: " + NUMBERS[j]->ErrorMessageText;
|
||||
LogFile.WriteToFile(_zw);
|
||||
}
|
||||
|
||||
SavePreValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ClassFlowPostProcessing::UpdateNachkommaDecimalShift()
|
||||
{
|
||||
for (int j = 0; j < NUMBERS.size(); ++j)
|
||||
{
|
||||
if (NUMBERS[j]->digit_roi && !NUMBERS[j]->analog_roi) // es gibt nur digitale ziffern
|
||||
{
|
||||
// printf("Nurdigital\n");
|
||||
NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial;
|
||||
|
||||
if (NUMBERS[j]->isExtendedResolution && flowDigit->isExtendedResolution()) // extended resolution ist an und soll auch bei dieser Ziffer verwendet werden
|
||||
NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShift-1;
|
||||
|
||||
NUMBERS[j]->Nachkomma = -NUMBERS[j]->DecimalShift;
|
||||
}
|
||||
|
||||
if (!NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) // es gibt nur analoge ziffern
|
||||
{
|
||||
// printf("Nur analog\n");
|
||||
NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial;
|
||||
if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) // extended resolution ist an und soll auch bei dieser Ziffer verwendet werden
|
||||
NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShift-1;
|
||||
|
||||
NUMBERS[j]->Nachkomma = -NUMBERS[j]->DecimalShift;
|
||||
}
|
||||
|
||||
if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi) // digital + analog
|
||||
{
|
||||
// printf("Nur digital + analog\n");
|
||||
|
||||
NUMBERS[j]->DecimalShift = NUMBERS[j]->DecimalShiftInitial;
|
||||
NUMBERS[j]->Nachkomma = NUMBERS[j]->analog_roi->ROI.size() - NUMBERS[j]->DecimalShift;
|
||||
|
||||
if (NUMBERS[j]->isExtendedResolution && flowAnalog->isExtendedResolution()) // extended resolution ist an und soll auch bei dieser Ziffer verwendet werden
|
||||
NUMBERS[j]->Nachkomma = NUMBERS[j]->Nachkomma+1;
|
||||
|
||||
}
|
||||
|
||||
printf("UpdateNachkommaDecShift NUMBER%i: Nachkomma %i, DecShift %i\n", j, NUMBERS[j]->Nachkomma,NUMBERS[j]->DecimalShift);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowPostProcessing::getReadout(int _number)
|
||||
{
|
||||
return NUMBERS[_number]->ReturnValue;
|
||||
@@ -717,14 +831,14 @@ float ClassFlowPostProcessing::checkDigitConsistency(float input, int _decilamsh
|
||||
while (pot <= pot_max)
|
||||
{
|
||||
zw = input / pow(10, pot-1);
|
||||
aktdigit_before = ((int) zw) % 10;
|
||||
aktdigit_before = ((int) zw + 10) % 10;
|
||||
zw = _preValue / pow(10, pot-1);
|
||||
olddigit_before = ((int) zw) % 10;
|
||||
olddigit_before = ((int) zw + 10) % 10;
|
||||
|
||||
zw = input / pow(10, pot);
|
||||
aktdigit = ((int) zw) % 10;
|
||||
aktdigit = ((int) zw + 10) % 10;
|
||||
zw = _preValue / pow(10, pot);
|
||||
olddigit = ((int) zw) % 10;
|
||||
olddigit = ((int) zw + 10) % 10;
|
||||
|
||||
no_nulldurchgang = (olddigit_before <= aktdigit_before);
|
||||
|
||||
|
||||
@@ -1,49 +1,13 @@
|
||||
#pragma once
|
||||
#ifndef __FLOWPOSTPROCESSING__
|
||||
#define __FLOWPOSTPROCESSING__
|
||||
|
||||
#include "ClassFlow.h"
|
||||
#include "ClassFlowMakeImage.h"
|
||||
#include "ClassFlowAnalog.h"
|
||||
#include "ClassFlowDigit.h"
|
||||
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowDefineTypes.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
struct NumberPost {
|
||||
// int PreValueAgeStartup;
|
||||
float MaxRateValue;
|
||||
bool useMaxRateValue;
|
||||
bool ErrorMessage;
|
||||
bool PreValueOkay;
|
||||
bool AllowNegativeRates;
|
||||
bool checkDigitIncreaseConsistency;
|
||||
time_t lastvalue;
|
||||
string timeStamp;
|
||||
float FlowRateAct; // m3 / min
|
||||
float PreValue; // letzter Wert, der gut ausgelesen wurde
|
||||
float Value; // letzer ausgelesener Wert, inkl. Korrekturen
|
||||
string ReturnRawValue; // Rohwert (mit N & führenden 0)
|
||||
string ReturnValue; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
|
||||
string ReturnPreValue; // korrigierter Rückgabewert ohne Fehlermeldung
|
||||
string ReturnValueNoError;
|
||||
string ErrorMessageText; // Fehlermeldung bei Consistency Check
|
||||
int AnzahlAnalog;
|
||||
int AnzahlDigital;
|
||||
int DecimalShift;
|
||||
int Nachkomma;
|
||||
// ClassFlowAnalog* ANALOG;
|
||||
// ClassFlowDigit* DIGIT;
|
||||
|
||||
digit *digit_roi;
|
||||
analog *analog_roi;
|
||||
|
||||
|
||||
|
||||
string name;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class ClassFlowPostProcessing :
|
||||
public ClassFlow
|
||||
{
|
||||
@@ -51,13 +15,13 @@ protected:
|
||||
std::vector<NumberPost*> NUMBERS;
|
||||
bool UpdatePreValueINI;
|
||||
|
||||
bool PreValueUse;
|
||||
int PreValueAgeStartup;
|
||||
bool ErrorMessage;
|
||||
bool IgnoreLeadingNaN; // SPEZIALFALL für User Gustl
|
||||
|
||||
|
||||
ClassFlowAnalog* flowAnalog;
|
||||
ClassFlowDigit* flowDigit;
|
||||
ClassFlowCNNGeneral* flowAnalog;
|
||||
ClassFlowCNNGeneral* flowDigit;
|
||||
|
||||
|
||||
string FilePreValue;
|
||||
@@ -74,10 +38,14 @@ protected:
|
||||
void InitNUMBERS();
|
||||
void handleDecimalSeparator(string _decsep, string _value);
|
||||
void handleMaxRateValue(string _decsep, string _value);
|
||||
void handleDecimalExtendedResolution(string _decsep, string _value);
|
||||
|
||||
|
||||
|
||||
public:
|
||||
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc);
|
||||
bool PreValueUse;
|
||||
|
||||
ClassFlowPostProcessing(std::vector<ClassFlow*>* lfc, ClassFlowCNNGeneral *_analog, ClassFlowCNNGeneral *_digit);
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string getReadout(int _number);
|
||||
@@ -87,9 +55,14 @@ public:
|
||||
string getReadoutTimeStamp(int _number = 0);
|
||||
void SavePreValue();
|
||||
string GetPreValue(std::string _number = "");
|
||||
void SetPreValue(float zw, string _numbers);
|
||||
std::vector<NumberPost*> GetNumbers(){return NUMBERS;};
|
||||
void SetPreValue(float zw, string _numbers, bool _extern = false);
|
||||
|
||||
void UpdateNachkommaDecimalShift();
|
||||
|
||||
std::vector<NumberPost*>* GetNumbers(){return &NUMBERS;};
|
||||
|
||||
string name(){return "ClassFlowPostProcessing";};
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES tfmicro jomjol_logfile)
|
||||
REQUIRES tflite-lib jomjol_logfile)
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,15 @@
|
||||
#include "Helper.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
@@ -80,20 +88,23 @@ void memCopyGen(uint8_t* _source, uint8_t* _target, int _size)
|
||||
|
||||
FILE* OpenFileAndWait(const char* nm, const char* _mode, int _waitsec)
|
||||
{
|
||||
printf("open config file %s in mode %s\n", nm, _mode);
|
||||
printf("open file %s in mode %s\n", nm, _mode);
|
||||
FILE *pfile = fopen(nm, _mode);
|
||||
|
||||
/*
|
||||
if (pfile == NULL)
|
||||
{
|
||||
TickType_t xDelay;
|
||||
xDelay = _waitsec * 1000 / portTICK_PERIOD_MS;
|
||||
std::string zw = "File is locked: " + std::string(nm) + " - wait for " + std::to_string(_waitsec);
|
||||
std::string zw = "File is locked: " + std::string(nm) + " - wait for " + std::to_string(_waitsec) + " seconds";
|
||||
printf(zw.c_str());
|
||||
printf("\n");
|
||||
LogFile.WriteToFile(zw);
|
||||
vTaskDelay( xDelay );
|
||||
pfile = fopen(nm, _mode);
|
||||
}
|
||||
*/
|
||||
|
||||
return pfile;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "CImageBasis.h"
|
||||
#include "Helper.h"
|
||||
#include "ClassLogFile.h"
|
||||
#include "server_ota.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
@@ -337,6 +338,18 @@ void CImageBasis::LoadFromMemory(stbi_uc *_buffer, int len)
|
||||
rgb_image = stbi_load_from_memory(_buffer, len, &width, &height, &channels, 3);
|
||||
bpp = channels;
|
||||
printf("Image loaded from memory: %d, %d, %d\n", width, height, channels);
|
||||
if ((width * height * channels) == 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Image with size 0 loaded --> reboot to be done! "
|
||||
"Check that your camera module is working and connected properly.");
|
||||
|
||||
LogFile.SwitchOnOff(true);
|
||||
LogFile.WriteToFile("Image with size 0 loaded --> reboot to be done! "
|
||||
"Check that your camera module is working and connected properly.");
|
||||
|
||||
doReboot();
|
||||
|
||||
}
|
||||
RGBImageRelease();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES jomjol_helper jomjol_logfile esp_http_server)
|
||||
REQUIRES jomjol_helper jomjol_logfile esp_http_server jomjol_fileserver_ota)
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,15 @@
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <dirent.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "Helper.h"
|
||||
|
||||
static const char *TAG = "log";
|
||||
|
||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES tfmicro mqtt jomjol_logfile)
|
||||
REQUIRES tflite-lib mqtt jomjol_logfile)
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ float CTfLiteClass::GetOutputValue(int nr)
|
||||
return output2->data.f[nr];
|
||||
}
|
||||
|
||||
|
||||
int CTfLiteClass::GetClassFromImageBasis(CImageBasis *rs)
|
||||
{
|
||||
if (!LoadInputImageBasis(rs))
|
||||
@@ -27,19 +28,36 @@ int CTfLiteClass::GetClassFromImageBasis(CImageBasis *rs)
|
||||
return GetOutClassification();
|
||||
}
|
||||
|
||||
int CTfLiteClass::GetOutClassification()
|
||||
|
||||
int CTfLiteClass::GetOutClassification(int _von, int _bis)
|
||||
{
|
||||
TfLiteTensor* output2 = interpreter->output(0);
|
||||
|
||||
float zw_max = 0;
|
||||
float zw_max;
|
||||
float zw;
|
||||
int zw_class = -1;
|
||||
int zw_class;
|
||||
|
||||
if (output2 == NULL)
|
||||
return -1;
|
||||
|
||||
int numeroutput = output2->dims->data[1];
|
||||
for (int i = 0; i < numeroutput; ++i)
|
||||
//printf("\n number output neurons: %d\n\n", numeroutput);
|
||||
|
||||
if (_bis == -1)
|
||||
_bis = numeroutput -1;
|
||||
|
||||
if (_von == -1)
|
||||
_von = 0;
|
||||
|
||||
if (_bis >= numeroutput)
|
||||
{
|
||||
printf("ANZAHL OUTPUT NEURONS passt nicht zu geforderter Classifizierung!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
zw_max = output2->data.f[_von];
|
||||
zw_class = _von;
|
||||
for (int i = _von + 1; i <= _bis; ++i)
|
||||
{
|
||||
zw = output2->data.f[i];
|
||||
if (zw > zw_max)
|
||||
@@ -48,7 +66,7 @@ int CTfLiteClass::GetOutClassification()
|
||||
zw_class = i;
|
||||
}
|
||||
}
|
||||
return zw_class;
|
||||
return (zw_class - _von);
|
||||
}
|
||||
|
||||
void CTfLiteClass::GetInputDimension(bool silent = false)
|
||||
@@ -70,18 +88,18 @@ void CTfLiteClass::GetInputDimension(bool silent = false)
|
||||
}
|
||||
|
||||
|
||||
void CTfLiteClass::GetOutPut()
|
||||
int CTfLiteClass::GetAnzOutPut(bool silent)
|
||||
{
|
||||
TfLiteTensor* output2 = this->interpreter->output(0);
|
||||
|
||||
int numdim = output2->dims->size;
|
||||
printf("NumDimension: %d\n", numdim);
|
||||
if (!silent) printf("NumDimension: %d\n", numdim);
|
||||
|
||||
int sizeofdim;
|
||||
for (int j = 0; j < numdim; ++j)
|
||||
{
|
||||
sizeofdim = output2->dims->data[j];
|
||||
printf("SizeOfDimension %d: %d\n", j, sizeofdim);
|
||||
if (!silent) printf("SizeOfDimension %d: %d\n", j, sizeofdim);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,8 +110,9 @@ void CTfLiteClass::GetOutPut()
|
||||
for (int i = 0; i < numeroutput; ++i)
|
||||
{
|
||||
fo = output2->data.f[i];
|
||||
printf("Result %d: %f\n", i, fo);
|
||||
if (!silent) printf("Result %d: %f\n", i, fo);
|
||||
}
|
||||
return numeroutput;
|
||||
}
|
||||
|
||||
void CTfLiteClass::Invoke()
|
||||
@@ -106,7 +125,7 @@ void CTfLiteClass::Invoke()
|
||||
|
||||
bool CTfLiteClass::LoadInputImageBasis(CImageBasis *rs)
|
||||
{
|
||||
std::string zw = "ClassFlowAnalog::doNeuralNetwork nach LoadInputResizeImage: ";
|
||||
std::string zw = "ClassFlowCNNGeneral::doNeuralNetwork nach LoadInputResizeImage: ";
|
||||
|
||||
unsigned int w = rs->width;
|
||||
unsigned int h = rs->height;
|
||||
@@ -149,6 +168,8 @@ void CTfLiteClass::MakeAllocate()
|
||||
TfLiteStatus allocate_status = this->interpreter->AllocateTensors();
|
||||
if (allocate_status != kTfLiteOk) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
|
||||
LogFile.WriteToFile("AllocateTensors() failed");
|
||||
|
||||
this->GetInputDimension();
|
||||
return;
|
||||
}
|
||||
@@ -215,14 +236,13 @@ bool CTfLiteClass::LoadModel(std::string _fn){
|
||||
this->error_reporter = new tflite::MicroErrorReporter;
|
||||
#endif
|
||||
|
||||
unsigned char *rd;
|
||||
rd = ReadFileToCharArray(_fn.c_str());
|
||||
modelload = ReadFileToCharArray(_fn.c_str());
|
||||
|
||||
if (rd == NULL)
|
||||
if (modelload == NULL)
|
||||
return false;
|
||||
|
||||
this->model = tflite::GetModel(rd);
|
||||
free(rd);
|
||||
model = tflite::GetModel(modelload);
|
||||
// free(rd);
|
||||
TFLITE_MINIMAL_CHECK(model != nullptr);
|
||||
|
||||
return true;
|
||||
@@ -236,7 +256,7 @@ CTfLiteClass::CTfLiteClass()
|
||||
this->interpreter = nullptr;
|
||||
this->input = nullptr;
|
||||
this->output = nullptr;
|
||||
this->kTensorArenaSize = 200 * 1024; /// laut testfile: 108000 - bisher 600
|
||||
this->kTensorArenaSize = 800 * 1024; /// laut testfile: 108000 - bisher 600;; 2021-09-11: 200 * 1024
|
||||
this->tensor_arena = new uint8_t[kTensorArenaSize];
|
||||
}
|
||||
|
||||
@@ -245,6 +265,8 @@ CTfLiteClass::~CTfLiteClass()
|
||||
delete this->tensor_arena;
|
||||
delete this->interpreter;
|
||||
delete this->error_reporter;
|
||||
if (modelload)
|
||||
free(modelload);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ class CTfLiteClass
|
||||
int kTensorArenaSize;
|
||||
uint8_t *tensor_arena;
|
||||
|
||||
unsigned char *modelload = NULL;
|
||||
|
||||
|
||||
float* input;
|
||||
int input_i;
|
||||
int im_height, im_width, im_channel;
|
||||
@@ -61,9 +64,13 @@ class CTfLiteClass
|
||||
void GetInputTensorSize();
|
||||
bool LoadInputImageBasis(CImageBasis *rs);
|
||||
void Invoke();
|
||||
void GetOutPut();
|
||||
int GetOutClassification();
|
||||
int GetAnzOutPut(bool silent = true);
|
||||
// void GetOutPut();
|
||||
// int GetOutClassification();
|
||||
int GetOutClassification(int _von = -1, int _bis = -1);
|
||||
|
||||
int GetClassFromImageBasis(CImageBasis *rs);
|
||||
std::string GetStatusFlow();
|
||||
|
||||
float GetOutputValue(int nr);
|
||||
void GetInputDimension(bool silent);
|
||||
|
||||
@@ -28,9 +28,6 @@ ClassFlowControll tfliteflow;
|
||||
TaskHandle_t xHandleblink_task_doFlow = NULL;
|
||||
TaskHandle_t xHandletask_autodoFlow = NULL;
|
||||
|
||||
|
||||
|
||||
|
||||
bool flowisrunning = false;
|
||||
|
||||
long auto_intervall = 0;
|
||||
@@ -192,6 +189,36 @@ esp_err_t handler_doflow(httpd_req_t *req)
|
||||
};
|
||||
|
||||
|
||||
esp_err_t handler_json(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_json - Start");
|
||||
#endif
|
||||
|
||||
|
||||
printf("handler_JSON uri:\n"); printf(req->uri); printf("\n");
|
||||
|
||||
char _query[100];
|
||||
char _size[10];
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
|
||||
std::string zw = tfliteflow.getJSON();
|
||||
if (zw.length() > 0)
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
|
||||
string query = std::string(_query);
|
||||
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_JSON - Done");
|
||||
#endif
|
||||
return ESP_OK;
|
||||
};
|
||||
|
||||
|
||||
|
||||
esp_err_t handler_wasserzaehler(httpd_req_t *req)
|
||||
@@ -283,37 +310,48 @@ esp_err_t handler_wasserzaehler(httpd_req_t *req)
|
||||
txt = txt + "Digital Counter: <p> ";
|
||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
htmlinfo = tfliteflow.GetAllDigital();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
std::vector<HTMLInfo*> htmlinfodig;
|
||||
htmlinfodig = tfliteflow.GetAllDigital();
|
||||
for (int i = 0; i < htmlinfodig.size(); ++i)
|
||||
{
|
||||
if (htmlinfo[i]->val == 10)
|
||||
zw = "NaN";
|
||||
if (tfliteflow.GetTypeDigital() == Digital)
|
||||
{
|
||||
if (htmlinfodig[i]->val == 10)
|
||||
zw = "NaN";
|
||||
else
|
||||
zw = to_string((int) htmlinfodig[i]->val);
|
||||
|
||||
txt = "<img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"> " + zw;
|
||||
}
|
||||
else
|
||||
{
|
||||
zw = to_string((int) htmlinfo[i]->val);
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfodig[i]->val;
|
||||
zw = stream.str();
|
||||
|
||||
txt = "<img src=\"/img_tmp/" + htmlinfodig[i]->filename + "\"> " + zw;
|
||||
}
|
||||
txt = "<img src=\"/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||
delete htmlinfo[i];
|
||||
delete htmlinfodig[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
htmlinfodig.clear();
|
||||
|
||||
txt = " <p> Analog Meter: <p> ";
|
||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||
|
||||
htmlinfo = tfliteflow.GetAllAnalog();
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
std::vector<HTMLInfo*> htmlinfoana;
|
||||
htmlinfoana = tfliteflow.GetAllAnalog();
|
||||
for (int i = 0; i < htmlinfoana.size(); ++i)
|
||||
{
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfo[i]->val;
|
||||
stream << std::fixed << std::setprecision(1) << htmlinfoana[i]->val;
|
||||
zw = stream.str();
|
||||
|
||||
txt = "<img src=\"/img_tmp/" + htmlinfo[i]->filename + "\"> " + zw;
|
||||
txt = "<img src=\"/img_tmp/" + htmlinfoana[i]->filename + "\"> " + zw;
|
||||
httpd_resp_sendstr_chunk(req, txt.c_str());
|
||||
delete htmlinfo[i];
|
||||
delete htmlinfoana[i];
|
||||
}
|
||||
htmlinfo.clear();
|
||||
htmlinfoana.clear();
|
||||
|
||||
}
|
||||
|
||||
@@ -493,31 +531,7 @@ esp_err_t handler_editflow(httpd_req_t *req)
|
||||
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
|
||||
std::string zw = tfliteflow.doSingleStep("[Alignment]", _host);
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
}
|
||||
if (_task.compare("test_analog") == 0)
|
||||
{
|
||||
std::string _host = "";
|
||||
if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) {
|
||||
_host = std::string(_valuechar);
|
||||
}
|
||||
// printf("Parameter host: "); printf(_host.c_str()); printf("\n");
|
||||
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
|
||||
std::string zw = tfliteflow.doSingleStep("[Analog]", _host);
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
}
|
||||
if (_task.compare("test_digits") == 0)
|
||||
{
|
||||
std::string _host = "";
|
||||
if (httpd_query_key_value(_query, "host", _valuechar, 30) == ESP_OK) {
|
||||
_host = std::string(_valuechar);
|
||||
}
|
||||
// printf("Parameter host: "); printf(_host.c_str()); printf("\n");
|
||||
|
||||
// string zwzw = "Do " + _task + " start\n"; printf(zwzw.c_str());
|
||||
std::string zw = tfliteflow.doSingleStep("[Digits]", _host);
|
||||
httpd_resp_sendstr_chunk(req, zw.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
@@ -530,6 +544,34 @@ esp_err_t handler_editflow(httpd_req_t *req)
|
||||
};
|
||||
|
||||
|
||||
esp_err_t handler_statusflow(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_prevalue - Start");
|
||||
#endif
|
||||
|
||||
const char* resp_str;
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
printf("handler_prevalue:\n"); printf(req->uri); printf("\n");
|
||||
#endif
|
||||
|
||||
string* zw = tfliteflow.getActStatus();
|
||||
resp_str = zw->c_str();
|
||||
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
/* Respond with an empty chunk to signal HTTP response completion */
|
||||
httpd_resp_send_chunk(req, NULL, 0);
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("handler_prevalue - Start");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
};
|
||||
|
||||
|
||||
esp_err_t handler_prevalue(httpd_req_t *req)
|
||||
{
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
@@ -569,7 +611,7 @@ esp_err_t handler_prevalue(httpd_req_t *req)
|
||||
}
|
||||
else
|
||||
{
|
||||
zw = "SetPrevalue to " + tfliteflow.UpdatePrevalue(_size, _numbers);
|
||||
zw = "SetPrevalue to " + tfliteflow.UpdatePrevalue(_size, _numbers, true);
|
||||
}
|
||||
|
||||
resp_str = zw.c_str();
|
||||
@@ -652,7 +694,28 @@ void task_autodoFlow(void *pvParameter)
|
||||
|
||||
void TFliteDoAutoStart()
|
||||
{
|
||||
xTaskCreate(&task_autodoFlow, "task_autodoFlow", configMINIMAL_STACK_SIZE * 64, NULL, tskIDLE_PRIORITY+1, &xHandletask_autodoFlow);
|
||||
BaseType_t xReturned;
|
||||
|
||||
int _i = configMINIMAL_STACK_SIZE;
|
||||
|
||||
printf("task_autodoFlow configMINIMAL_STACK_SIZE: %d\n", _i);
|
||||
printf("getESPHeapInfo: %s\n", getESPHeapInfo().c_str());
|
||||
|
||||
xReturned = xTaskCreate(&task_autodoFlow, "task_autodoFlow", configMINIMAL_STACK_SIZE * 60, NULL, tskIDLE_PRIORITY+1, &xHandletask_autodoFlow);
|
||||
if( xReturned != pdPASS )
|
||||
{
|
||||
|
||||
//Memory: 64 --> 48 --> 35 --> 25
|
||||
printf("ERROR task_autodoFlow konnte nicht erzeugt werden !!\r\n");
|
||||
}
|
||||
printf("getESPHeapInfo: %s\n", getESPHeapInfo().c_str());
|
||||
|
||||
|
||||
}
|
||||
|
||||
std::string GetMQTTMainTopic()
|
||||
{
|
||||
return tfliteflow.GetMQTTMainTopic();
|
||||
}
|
||||
|
||||
|
||||
@@ -679,6 +742,10 @@ void register_server_tflite_uri(httpd_handle_t server)
|
||||
camuri.user_ctx = (void*) "Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/statusflow.html";
|
||||
camuri.handler = handler_statusflow;
|
||||
camuri.user_ctx = (void*) "Light Off";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/editflow.html";
|
||||
camuri.handler = handler_editflow;
|
||||
@@ -689,4 +756,10 @@ void register_server_tflite_uri(httpd_handle_t server)
|
||||
camuri.handler = handler_wasserzaehler;
|
||||
camuri.user_ctx = (void*) "Wasserzaehler";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
camuri.uri = "/json";
|
||||
camuri.handler = handler_json;
|
||||
camuri.user_ctx = (void*) "JSON";
|
||||
httpd_register_uri_handler(server, &camuri);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <esp_log.h>
|
||||
#include <string>
|
||||
|
||||
#include <esp_http_server.h>
|
||||
#include "CImageBasis.h"
|
||||
@@ -13,6 +14,8 @@ void TFliteDoAutoStart();
|
||||
|
||||
bool isSetupModusActive();
|
||||
|
||||
std::string GetMQTTMainTopic();
|
||||
|
||||
esp_err_t GetJPG(std::string _filename, httpd_req_t *req);
|
||||
|
||||
esp_err_t GetRawJPG(httpd_req_t *req);
|
||||
|
||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES tfmicro jomjol_logfile)
|
||||
REQUIRES tflite-lib jomjol_logfile)
|
||||
|
||||
|
||||
|
||||
@@ -50,14 +50,14 @@ std::string std_hostname = "watermeter";
|
||||
std::string ipadress = "";
|
||||
std::string ssid = "";
|
||||
|
||||
std::string getIPAddress()
|
||||
std::string* getIPAddress()
|
||||
{
|
||||
return ipadress;
|
||||
return &ipadress;
|
||||
}
|
||||
|
||||
std::string getSSID()
|
||||
std::string* getSSID()
|
||||
{
|
||||
return ssid;
|
||||
return &ssid;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,14 +115,10 @@ static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
LEDBlinkTask(200, 1, true);
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
|
||||
// LEDBlinkTask(200, 1, true);
|
||||
// if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY){
|
||||
esp_wifi_connect();
|
||||
s_retry_num++;
|
||||
ESP_LOGI(TAG, "retry to connect to the AP");
|
||||
} else {
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
}
|
||||
ESP_LOGI(TAG,"connect to the AP fail");
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
||||
@@ -188,32 +184,6 @@ void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostna
|
||||
ip_addr_set_ip4_u32(&dns_info.ip, ip.addr);
|
||||
ESP_ERROR_CHECK(esp_netif_set_dns_info(my_sta, ESP_NETIF_DNS_MAIN, &dns_info));
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// esp_netif_create_default_wifi_sta();
|
||||
|
||||
// wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
// ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
|
||||
|
||||
/*
|
||||
////////////////////////////// esp-idf 4.2 //////////////////////////
|
||||
esp_event_handler_instance_t instance_any_id;
|
||||
esp_event_handler_instance_t instance_got_ip;
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
ESP_EVENT_ANY_ID,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_any_id));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_got_ip));
|
||||
////////////////////////// ENDE esp-idf 4.2 ///////////////////////////
|
||||
*/
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
|
||||
@@ -257,27 +227,6 @@ void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostna
|
||||
} else {
|
||||
ESP_LOGE(TAG, "UNEXPECTED EVENT");
|
||||
}
|
||||
|
||||
/* The event will not be processed after unregister */
|
||||
/*
|
||||
////////////////////////////// esp-idf 4.2 //////////////////////////
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
|
||||
////////////////////////// ENDE esp-idf 4.2 ///////////////////////////
|
||||
*/
|
||||
|
||||
/* Deaktiveren, damit bei einen Verbindungsabbruch neu aufgebaut wird
|
||||
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
|
||||
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
|
||||
vEventGroupDelete(s_wifi_event_group);
|
||||
*/
|
||||
|
||||
/*
|
||||
while (BlinkIsRunning)
|
||||
{
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostna
|
||||
void wifi_init_sta(const char *_ssid, const char *_password, const char *_hostname);
|
||||
void wifi_init_sta(const char *_ssid, const char *_password);
|
||||
|
||||
std::string getIPAddress();
|
||||
std::string getSSID();
|
||||
std::string* getIPAddress();
|
||||
std::string* getSSID();
|
||||
|
||||
extern std::string hostname;
|
||||
extern std::string std_hostname;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <string.h>
|
||||
|
||||
|
||||
std::vector<string> ZerlegeZeile(std::string input, std::string _delimiter = "")
|
||||
std::vector<string> ZerlegeZeileWLAN(std::string input, std::string _delimiter = "")
|
||||
{
|
||||
std::vector<string> Output;
|
||||
std::string delimiter = " =,";
|
||||
@@ -23,13 +23,13 @@ std::vector<string> ZerlegeZeile(std::string input, std::string _delimiter = "")
|
||||
input = trim(input, delimiter);
|
||||
size_t pos = findDelimiterPos(input, delimiter);
|
||||
std::string token;
|
||||
while (pos != std::string::npos) {
|
||||
if (pos != std::string::npos) // Zerlegt nur bis ersten Gleichheitszeichen !!! Sonderfall für WLAN.ini
|
||||
{
|
||||
token = input.substr(0, pos);
|
||||
token = trim(token, delimiter);
|
||||
Output.push_back(token);
|
||||
input.erase(0, pos + 1);
|
||||
input = trim(input, delimiter);
|
||||
pos = findDelimiterPos(input, delimiter);
|
||||
}
|
||||
Output.push_back(input);
|
||||
|
||||
@@ -67,10 +67,8 @@ void LoadWlanFromFile(std::string fn, char *&_ssid, char *&_password, char *&_ho
|
||||
while ((line.size() > 0) || !(feof(pFile)))
|
||||
{
|
||||
// printf("%s", line.c_str());
|
||||
zerlegt = ZerlegeZeile(line, "=");
|
||||
zerlegt = ZerlegeZeileWLAN(line, "=");
|
||||
zerlegt[0] = trim(zerlegt[0], " ");
|
||||
for (int i = 2; i < zerlegt.size(); ++i)
|
||||
zerlegt[1] = zerlegt[1] + "=" + zerlegt[i];
|
||||
|
||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "HOSTNAME")){
|
||||
hostname = trim(zerlegt[1]);
|
||||
@@ -212,7 +210,7 @@ bool ChangeHostName(std::string fn, std::string _newhostname)
|
||||
while ((line.size() > 0) || !(feof(pFile)))
|
||||
{
|
||||
printf("%s", line.c_str());
|
||||
zerlegt = ZerlegeZeile(line, "=");
|
||||
zerlegt = ZerlegeZeileWLAN(line, "=");
|
||||
zerlegt[0] = trim(zerlegt[0], " ");
|
||||
|
||||
if ((zerlegt.size() > 1) && (toUpper(zerlegt[0]) == "HOSTNAME")){
|
||||
|
||||
50
code/components/tflite-lib/CMakeLists.txt
Normal file
50
code/components/tflite-lib/CMakeLists.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(tflite_dir "${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/lite")
|
||||
set(tfmicro_dir "${tflite_dir}/micro")
|
||||
set(tfmicro_frontend_dir "${tflite_dir}/experimental/microfrontend/lib")
|
||||
set(tfmicro_kernels_dir "${tfmicro_dir}/kernels")
|
||||
|
||||
file(GLOB srcs_micro
|
||||
"${tfmicro_dir}/*.cc"
|
||||
"${tfmicro_dir}/*.c")
|
||||
|
||||
file(GLOB src_micro_frontend
|
||||
"${tfmicro_frontend_dir}/*.c"
|
||||
"${tfmicro_frontend_dir}/*.cc")
|
||||
file(GLOB srcs_kernels
|
||||
"${tfmicro_kernels_dir}/*.c"
|
||||
"${tfmicro_kernels_dir}/*.cc")
|
||||
|
||||
set(lib_srcs
|
||||
"${srcs_micro}"
|
||||
"${srcs_kernels}"
|
||||
"${src_micro_frontend}"
|
||||
"${tflite_dir}/kernels/kernel_util.cc"
|
||||
"${tflite_dir}/micro/memory_planner/greedy_memory_planner.cc"
|
||||
"${tflite_dir}/micro/memory_planner/linear_memory_planner.cc"
|
||||
"${tflite_dir}/c/common.c"
|
||||
"${tflite_dir}/core/api/error_reporter.cc"
|
||||
"${tflite_dir}/core/api/flatbuffer_conversions.cc"
|
||||
"${tflite_dir}/core/api/op_resolver.cc"
|
||||
"${tflite_dir}/core/api/tensor_utils.cc"
|
||||
"${tflite_dir}/kernels/internal/quantization_util.cc"
|
||||
"${tflite_dir}/schema/schema_utils.cc")
|
||||
|
||||
idf_component_register(
|
||||
SRCS "${lib_srcs}"
|
||||
INCLUDE_DIRS "." "third_party/gemmlowp"
|
||||
"third_party/flatbuffers/include"
|
||||
"third_party/ruy"
|
||||
"third_party/kissfft")
|
||||
|
||||
# Reduce the level of paranoia to be able to compile TF sources
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE
|
||||
-Wno-maybe-uninitialized
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-type-limits)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DESP -DESP_NN -Wno-nonnull -Wno-nonnull -Wno-nonnull)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE $<$<COMPILE_LANGUAGE:CXX>: -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DESP -DESP_NN -Wno-return-type -Wno-strict-aliasing -std=gnu++14 -Wno-return-type -Wno-strict-aliasing -std=gnu++14 -Wno-return-type -Wno-strict-aliasing -std=gnu++14 >)
|
||||
target_compile_options(${COMPONENT_LIB} INTERFACE $<$<IN_LIST:-DTF_LITE_STATIC_MEMORY,$<TARGET_PROPERTY:${COMPONENT_LIB},COMPILE_OPTIONS>>:-DTF_LITE_STATIC_MEMORY>)
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE -lm)
|
||||
@@ -63,7 +63,6 @@ typedef struct {
|
||||
} TfLiteMirrorPaddingParams;
|
||||
|
||||
// Possible fused activation functions.
|
||||
// TODO(aselle): rename to TfLiteActivation
|
||||
typedef enum {
|
||||
kTfLiteActNone = 0,
|
||||
kTfLiteActRelu,
|
||||
@@ -98,6 +97,8 @@ typedef struct {
|
||||
TfLiteFusedActivation activation;
|
||||
} TfLiteConv3DParams;
|
||||
|
||||
typedef TfLiteConv3DParams TfLiteConv3DTransposeParams;
|
||||
|
||||
typedef struct {
|
||||
TfLitePadding padding;
|
||||
int stride_width;
|
||||
@@ -328,8 +329,9 @@ typedef struct {
|
||||
} TfLitePadV2Params;
|
||||
|
||||
typedef struct {
|
||||
// TODO(ahentz): We can't have dynamic data in this struct, at least not yet.
|
||||
// For now we will fix the maximum possible number of dimensions.
|
||||
// These fields are only used in old models for backward compatibility.
|
||||
// In the current implementation, we use the 2nd input of the op as the shape,
|
||||
// and these fields are unused.
|
||||
int shape[TFLITE_RESHAPE_PARAMS_MAX_DIMENSION_COUNT];
|
||||
int num_dimensions;
|
||||
} TfLiteReshapeParams;
|
||||
@@ -495,6 +497,27 @@ typedef struct {
|
||||
TfLiteType value_dtype;
|
||||
} TfLiteHashtableParams;
|
||||
|
||||
typedef struct {
|
||||
const char* container;
|
||||
const char* shared_name;
|
||||
} TfLiteVarHandleParams;
|
||||
|
||||
typedef struct {
|
||||
int seed;
|
||||
int seed2;
|
||||
} TfLiteRandomParams;
|
||||
|
||||
typedef struct {
|
||||
int num_boundaries;
|
||||
// This points to the memory stored in the model (flatbuffer),
|
||||
// and is not owned.
|
||||
const float* boundaries;
|
||||
} TfLiteBucketizeParams;
|
||||
|
||||
typedef struct {
|
||||
bool approximate;
|
||||
} TfLiteGeluParams;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
@@ -29,7 +29,9 @@ extern "C" {
|
||||
// library.
|
||||
#ifdef SWIG
|
||||
#define TFL_CAPI_EXPORT
|
||||
#else
|
||||
#elif defined(TFL_STATIC_LIBRARY_BUILD)
|
||||
#define TFL_CAPI_EXPORT
|
||||
#else // not definded TFL_STATIC_LIBRARY_BUILD
|
||||
#if defined(_WIN32)
|
||||
#ifdef TFL_COMPILE_LIBRARY
|
||||
#define TFL_CAPI_EXPORT __declspec(dllexport)
|
||||
@@ -41,6 +43,9 @@ extern "C" {
|
||||
#endif // _WIN32
|
||||
#endif // SWIG
|
||||
|
||||
// Note that new error status values may be added in future in order to
|
||||
// indicate more fine-grained internal states, therefore, applications should
|
||||
// not rely on status values being members of the enum.
|
||||
typedef enum TfLiteStatus {
|
||||
kTfLiteOk = 0,
|
||||
|
||||
@@ -52,9 +57,26 @@ typedef enum TfLiteStatus {
|
||||
|
||||
// Generally referring to an error in applying a delegate due to
|
||||
// incompatibility between runtime and delegate, e.g., this error is returned
|
||||
// when trying to apply a TfLite delegate onto a model graph that's already
|
||||
// when trying to apply a TF Lite delegate onto a model graph that's already
|
||||
// immutable.
|
||||
kTfLiteApplicationError = 3
|
||||
kTfLiteApplicationError = 3,
|
||||
|
||||
// Generally referring to serialized delegate data not being found.
|
||||
// See tflite::delegates::Serialization.
|
||||
kTfLiteDelegateDataNotFound = 4,
|
||||
|
||||
// Generally referring to data-writing issues in delegate serialization.
|
||||
// See tflite::delegates::Serialization.
|
||||
kTfLiteDelegateDataWriteError = 5,
|
||||
|
||||
// Generally referring to data-reading issues in delegate serialization.
|
||||
// See tflite::delegates::Serialization.
|
||||
kTfLiteDelegateDataReadError = 6,
|
||||
|
||||
// Generally referring to issues when the TF Lite model has ops that cannot be
|
||||
// resolved at runtime. This could happen when the specific op is not
|
||||
// registered or built with the TF Lite framework.
|
||||
kTfLiteUnresolvedOps = 7,
|
||||
} TfLiteStatus;
|
||||
|
||||
// Types supported by tensor
|
||||
@@ -21,9 +21,15 @@ limitations under the License.
|
||||
#include <string.h>
|
||||
#endif // TF_LITE_STATIC_MEMORY
|
||||
|
||||
int TfLiteIntArrayGetSizeInBytes(int size) {
|
||||
size_t TfLiteIntArrayGetSizeInBytes(int size) {
|
||||
static TfLiteIntArray dummy;
|
||||
return sizeof(dummy) + sizeof(dummy.data[0]) * size;
|
||||
|
||||
size_t computed_size = sizeof(dummy) + sizeof(dummy.data[0]) * size;
|
||||
#if defined(_MSC_VER)
|
||||
// Context for why this is needed is in http://b/189926408#comment21
|
||||
computed_size -= sizeof(dummy.data[0]);
|
||||
#endif
|
||||
return computed_size;
|
||||
}
|
||||
|
||||
int TfLiteIntArrayEqual(const TfLiteIntArray* a, const TfLiteIntArray* b) {
|
||||
@@ -45,8 +51,10 @@ int TfLiteIntArrayEqualsArray(const TfLiteIntArray* a, int b_size,
|
||||
#ifndef TF_LITE_STATIC_MEMORY
|
||||
|
||||
TfLiteIntArray* TfLiteIntArrayCreate(int size) {
|
||||
TfLiteIntArray* ret =
|
||||
(TfLiteIntArray*)malloc(TfLiteIntArrayGetSizeInBytes(size));
|
||||
size_t alloc_size = TfLiteIntArrayGetSizeInBytes(size);
|
||||
if (alloc_size <= 0) return NULL;
|
||||
TfLiteIntArray* ret = (TfLiteIntArray*)malloc(alloc_size);
|
||||
if (!ret) return ret;
|
||||
ret->size = size;
|
||||
return ret;
|
||||
}
|
||||
@@ -66,7 +74,13 @@ void TfLiteIntArrayFree(TfLiteIntArray* a) { free(a); }
|
||||
|
||||
int TfLiteFloatArrayGetSizeInBytes(int size) {
|
||||
static TfLiteFloatArray dummy;
|
||||
return sizeof(dummy) + sizeof(dummy.data[0]) * size;
|
||||
|
||||
int computed_size = sizeof(dummy) + sizeof(dummy.data[0]) * size;
|
||||
#if defined(_MSC_VER)
|
||||
// Context for why this is needed is in http://b/189926408#comment21
|
||||
computed_size -= sizeof(dummy.data[0]);
|
||||
#endif
|
||||
return computed_size;
|
||||
}
|
||||
|
||||
#ifndef TF_LITE_STATIC_MEMORY
|
||||
@@ -174,6 +188,26 @@ void TfLiteTensorReset(TfLiteType type, const char* name, TfLiteIntArray* dims,
|
||||
tensor->quantization.params = NULL;
|
||||
}
|
||||
|
||||
TfLiteStatus TfLiteTensorCopy(const TfLiteTensor* src, TfLiteTensor* dst) {
|
||||
if (!src || !dst)
|
||||
return kTfLiteOk;
|
||||
if (src->bytes != dst->bytes)
|
||||
return kTfLiteError;
|
||||
if (src == dst)
|
||||
return kTfLiteOk;
|
||||
|
||||
dst->type = src->type;
|
||||
if (dst->dims)
|
||||
TfLiteIntArrayFree(dst->dims);
|
||||
dst->dims = TfLiteIntArrayCopy(src->dims);
|
||||
memcpy(dst->data.raw, src->data.raw, src->bytes);
|
||||
dst->buffer_handle = src->buffer_handle;
|
||||
dst->data_is_stale = src->data_is_stale;
|
||||
dst->delegate = src->delegate;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor* tensor) {
|
||||
if (tensor->allocation_type != kTfLiteDynamic &&
|
||||
tensor->allocation_type != kTfLitePersistentRo) {
|
||||
@@ -181,9 +215,9 @@ void TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor* tensor) {
|
||||
}
|
||||
// TODO(b/145340303): Tensor data should be aligned.
|
||||
if (!tensor->data.raw) {
|
||||
tensor->data.raw = malloc(num_bytes);
|
||||
tensor->data.raw = (char*)malloc(num_bytes);
|
||||
} else if (num_bytes > tensor->bytes) {
|
||||
tensor->data.raw = realloc(tensor->data.raw, num_bytes);
|
||||
tensor->data.raw = (char*)realloc(tensor->data.raw, num_bytes);
|
||||
}
|
||||
tensor->bytes = num_bytes;
|
||||
}
|
||||
@@ -229,7 +263,7 @@ const char* TfLiteTypeGetName(TfLiteType type) {
|
||||
return "Unknown type";
|
||||
}
|
||||
|
||||
TfLiteDelegate TfLiteDelegateCreate() {
|
||||
TfLiteDelegate TfLiteDelegateCreate(void) {
|
||||
TfLiteDelegate d = {
|
||||
.data_ = NULL,
|
||||
.Prepare = NULL,
|
||||
@@ -80,12 +80,16 @@ typedef struct TfLiteExternalContext {
|
||||
// indices
|
||||
typedef struct TfLiteIntArray {
|
||||
int size;
|
||||
// gcc 6.1+ have a bug where flexible members aren't properly handled
|
||||
// https://github.com/google/re2/commit/b94b7cd42e9f02673cd748c1ac1d16db4052514c
|
||||
#if (!defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && \
|
||||
__GNUC_MINOR__ >= 1) || \
|
||||
defined(HEXAGON) || \
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
// Context for why this is needed is in http://b/189926408#comment21
|
||||
int data[1];
|
||||
#elif (!defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && \
|
||||
__GNUC_MINOR__ >= 1) || \
|
||||
defined(HEXAGON) || \
|
||||
(defined(__clang__) && __clang_major__ == 7 && __clang_minor__ == 1)
|
||||
// gcc 6.1+ have a bug where flexible members aren't properly handled
|
||||
// https://github.com/google/re2/commit/b94b7cd42e9f02673cd748c1ac1d16db4052514c
|
||||
int data[0];
|
||||
#else
|
||||
int data[];
|
||||
@@ -94,7 +98,7 @@ typedef struct TfLiteIntArray {
|
||||
|
||||
// Given the size (number of elements) in a TfLiteIntArray, calculate its size
|
||||
// in bytes.
|
||||
int TfLiteIntArrayGetSizeInBytes(int size);
|
||||
size_t TfLiteIntArrayGetSizeInBytes(int size);
|
||||
|
||||
#ifndef TF_LITE_STATIC_MEMORY
|
||||
// Create a array of a given `size` (uninitialized entries).
|
||||
@@ -121,11 +125,15 @@ void TfLiteIntArrayFree(TfLiteIntArray* a);
|
||||
// Fixed size list of floats. Used for per-channel quantization.
|
||||
typedef struct TfLiteFloatArray {
|
||||
int size;
|
||||
// gcc 6.1+ have a bug where flexible members aren't properly handled
|
||||
// https://github.com/google/re2/commit/b94b7cd42e9f02673cd748c1ac1d16db4052514c
|
||||
// This also applies to the toolchain used for Qualcomm Hexagon DSPs.
|
||||
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && \
|
||||
__GNUC_MINOR__ >= 1
|
||||
#if defined(_MSC_VER)
|
||||
// Context for why this is needed is in http://b/189926408#comment21
|
||||
float data[1];
|
||||
#elif (!defined(__clang__) && defined(__GNUC__) && __GNUC__ == 6 && \
|
||||
__GNUC_MINOR__ >= 1) || \
|
||||
defined(HEXAGON) || \
|
||||
(defined(__clang__) && __clang_major__ == 7 && __clang_minor__ == 1)
|
||||
// gcc 6.1+ have a bug where flexible members aren't properly handled
|
||||
// https://github.com/google/re2/commit/b94b7cd42e9f02673cd748c1ac1d16db4052514c
|
||||
float data[0];
|
||||
#else
|
||||
float data[];
|
||||
@@ -456,8 +464,8 @@ typedef struct TfLiteTensor {
|
||||
} TfLiteTensor;
|
||||
|
||||
// A structure representing an instance of a node.
|
||||
// This structure only exhibits the inputs, outputs and user defined data, not
|
||||
// other features like the type.
|
||||
// This structure only exhibits the inputs, outputs, user defined data and some
|
||||
// node properties (like statefulness), not other features like the type.
|
||||
typedef struct TfLiteNode {
|
||||
// Inputs to this node expressed as indices into the simulator's tensors.
|
||||
TfLiteIntArray* inputs;
|
||||
@@ -490,6 +498,9 @@ typedef struct TfLiteNode {
|
||||
// created by calling `interpreter.ModifyGraphWithDelegate`.
|
||||
// WARNING: This is an experimental interface that is subject to change.
|
||||
struct TfLiteDelegate* delegate;
|
||||
|
||||
// Whether this op might have side effect (e.g. stateful op).
|
||||
bool might_have_side_effect;
|
||||
} TfLiteNode;
|
||||
#else // defined(TF_LITE_STATIC_MEMORY)?
|
||||
// NOTE: This flag is opt-in only at compile time.
|
||||
@@ -559,6 +570,10 @@ typedef struct TfLiteNode {
|
||||
// Outputs to this node expressed as indices into the simulator's tensors.
|
||||
TfLiteIntArray* outputs;
|
||||
|
||||
// intermediate tensors to this node expressed as indices into the simulator's
|
||||
// tensors.
|
||||
TfLiteIntArray* intermediates;
|
||||
|
||||
// Opaque data provided by the node implementer through `Registration.init`.
|
||||
void* user_data;
|
||||
|
||||
@@ -611,6 +626,16 @@ void TfLiteTensorReset(TfLiteType type, const char* name, TfLiteIntArray* dims,
|
||||
const void* allocation, bool is_variable,
|
||||
TfLiteTensor* tensor);
|
||||
|
||||
// Copies the contents of 'src' in 'dst'.
|
||||
// Function does nothing if either 'src' or 'dst' is passed as nullptr and
|
||||
// return kTfLiteOk.
|
||||
// Returns kTfLiteError if 'src' and 'dst' doesn't have matching data size.
|
||||
// Note function copies contents, so it won't create new data pointer
|
||||
// or change allocation type.
|
||||
// All Tensor related properties will be copied from 'src' to 'dst' like
|
||||
// quantization, sparsity, ...
|
||||
TfLiteStatus TfLiteTensorCopy(const TfLiteTensor* src, TfLiteTensor* dst);
|
||||
|
||||
// Resize the allocated data of a (dynamic) tensor. Tensors with allocation
|
||||
// types other than kTfLiteDynamic will be ignored.
|
||||
void TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor* tensor);
|
||||
@@ -640,6 +665,7 @@ typedef struct TfLiteContext {
|
||||
// TfLiteDelegates can traverse the current execution plan by iterating
|
||||
// through each member of this array and using GetNodeAndRegistration() to
|
||||
// access details about a node. i.e.
|
||||
//
|
||||
// TfLiteIntArray* execution_plan;
|
||||
// TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &execution_plan));
|
||||
// for (int exec_index = 0; exec_index < execution_plan->size; exec_index++) {
|
||||
@@ -648,6 +674,28 @@ typedef struct TfLiteContext {
|
||||
// TfLiteRegistration* reg;
|
||||
// context->GetNodeAndRegistration(context, node_index, &node, ®);
|
||||
// }
|
||||
// Note: the memory pointed by '`*execution_plan` is OWNED by TfLite runtime.
|
||||
// Future calls to GetExecutionPlan invalidates earlier outputs. The following
|
||||
// code snippet shows the issue of such an invocation pattern. After calling
|
||||
// CheckNode, subsequent access to `plan_1st` is undefined.
|
||||
//
|
||||
// void CheckNode(const TfLiteNode* node) {
|
||||
// ...
|
||||
// TfLiteIntArray* plan_2nd;
|
||||
// TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &plan_2nd));
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// TfLiteIntArray* plan_1st;
|
||||
// TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &plan_1st));
|
||||
// for (int exec_index = 0; exec_index < plan_1st->size; exec_index++) {
|
||||
// int node_index = plan_1st->data[exec_index];
|
||||
// TfLiteNode* node;
|
||||
// TfLiteRegistration* reg;
|
||||
// context->GetNodeAndRegistration(context, node_index, &node, ®);
|
||||
// CheckNode(node);
|
||||
// }
|
||||
//
|
||||
// WARNING: This is an experimental interface that is subject to change.
|
||||
TfLiteStatus (*GetExecutionPlan)(struct TfLiteContext* context,
|
||||
TfLiteIntArray** execution_plan);
|
||||
@@ -777,10 +825,25 @@ typedef struct TfLiteContext {
|
||||
// WARNING: This method may not be available on all platforms.
|
||||
TfLiteEvalTensor* (*GetEvalTensor)(const struct TfLiteContext* context,
|
||||
int tensor_idx);
|
||||
|
||||
// Retrieves named metadata buffer from the TFLite model.
|
||||
// Returns kTfLiteOk if metadata is successfully obtained from the flatbuffer
|
||||
// Model: that is, there exists a `metadata` entry with given `name` string.
|
||||
// (see TFLite's schema.fbs).
|
||||
// The corresponding `buffer` information is populated in `ptr` & `bytes`.
|
||||
// The data from `ptr` is valid for the lifetime of the Interpreter.
|
||||
//
|
||||
// WARNING: This is an experimental interface that is subject to change.
|
||||
TfLiteStatus (*GetModelMetadata)(const struct TfLiteContext* context,
|
||||
const char* name, const char** ptr,
|
||||
size_t* bytes);
|
||||
} TfLiteContext;
|
||||
|
||||
typedef struct TfLiteRegistration {
|
||||
// Initializes the op from serialized data.
|
||||
// Called only *once* for the lifetime of the op, so any one-time allocations
|
||||
// should be made here (unless they depend on tensor sizes).
|
||||
//
|
||||
// If a built-in op:
|
||||
// `buffer` is the op's params data (TfLiteLSTMParams*).
|
||||
// `length` is zero.
|
||||
@@ -803,6 +866,7 @@ typedef struct TfLiteRegistration {
|
||||
// prepare is called when the inputs this node depends on have been resized.
|
||||
// context->ResizeTensor() can be called to request output tensors to be
|
||||
// resized.
|
||||
// Can be called multiple times for the lifetime of the op.
|
||||
//
|
||||
// Returns kTfLiteOk on success.
|
||||
TfLiteStatus (*prepare)(TfLiteContext* context, TfLiteNode* node);
|
||||
@@ -918,7 +982,7 @@ typedef struct TfLiteDelegate {
|
||||
|
||||
// Build a 'null' delegate, with all the fields properly set to their default
|
||||
// values.
|
||||
TfLiteDelegate TfLiteDelegateCreate();
|
||||
TfLiteDelegate TfLiteDelegateCreate(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
/* Copyright 2021 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.
|
||||
@@ -131,6 +131,17 @@ TfLitePadding ConvertPadding(Padding padding) {
|
||||
return kTfLitePaddingUnknown;
|
||||
}
|
||||
|
||||
// Converts the flatbuffer mirror padding enum to what is used at runtime.
|
||||
TfLiteMirrorPaddingMode ConvertMirrorPadding(MirrorPadMode padding) {
|
||||
switch (padding) {
|
||||
case MirrorPadMode_REFLECT:
|
||||
return kTfLiteMirrorPaddingReflect;
|
||||
case MirrorPadMode_SYMMETRIC:
|
||||
return kTfLiteMirrorPaddingSymmetric;
|
||||
}
|
||||
return kTfLiteMirrorPaddingUnknown;
|
||||
}
|
||||
|
||||
#ifndef TF_LITE_STATIC_MEMORY
|
||||
TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
ErrorReporter* error_reporter,
|
||||
@@ -181,6 +192,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
return ParseArgMin(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_ASSIGN_VARIABLE: {
|
||||
return ParseAssignVariable(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_AVERAGE_POOL_2D: {
|
||||
return ParsePool(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
@@ -193,6 +208,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
return ParseBatchToSpaceNd(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_CALL_ONCE: {
|
||||
return ParseCallOnce(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_CEIL: {
|
||||
return ParseCeil(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
@@ -325,6 +344,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
return ParsePool(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_MIRROR_PAD: {
|
||||
return ParseMirrorPad(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_MEAN: {
|
||||
return ParseReducer(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
@@ -369,10 +392,18 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
return ParseQuantize(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_READ_VARIABLE: {
|
||||
return ParseReadVariable(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_REDUCE_ANY: {
|
||||
return ParseReducer(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_REDUCE_ALL: {
|
||||
return ParseReducer(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_REDUCE_MAX: {
|
||||
return ParseReducer(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
@@ -482,6 +513,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
return ParseUnpack(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_VAR_HANDLE: {
|
||||
return ParseVarHandle(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
|
||||
case BuiltinOperator_ZEROS_LIKE: {
|
||||
return ParseZerosLike(op, error_reporter, allocator, builtin_data);
|
||||
}
|
||||
@@ -602,21 +637,8 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM: {
|
||||
auto params =
|
||||
safe_allocator.Allocate<TfLiteUnidirectionalSequenceLSTMParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
if (const auto* seq_lstm_params =
|
||||
op->builtin_options_as_UnidirectionalSequenceLSTMOptions()) {
|
||||
params->activation =
|
||||
ConvertActivation(seq_lstm_params->fused_activation_function());
|
||||
params->cell_clip = seq_lstm_params->cell_clip();
|
||||
params->proj_clip = seq_lstm_params->proj_clip();
|
||||
params->time_major = seq_lstm_params->time_major();
|
||||
params->asymmetric_quantize_inputs =
|
||||
seq_lstm_params->asymmetric_quantize_inputs();
|
||||
}
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
return ParseUnidirectionalSequenceLSTM(op, error_reporter, allocator,
|
||||
builtin_data);
|
||||
}
|
||||
case BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM: {
|
||||
auto params =
|
||||
@@ -663,7 +685,6 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_DELEGATE: {
|
||||
// TODO(ycling): Revisit when supporting saving delegated models.
|
||||
TF_LITE_REPORT_ERROR(error_reporter,
|
||||
"DELEGATE op shouldn't exist in model.");
|
||||
return kTfLiteError;
|
||||
@@ -690,19 +711,6 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_MIRROR_PAD: {
|
||||
auto params = safe_allocator.Allocate<TfLiteMirrorPaddingParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
const auto* mirror_pad_params = op->builtin_options_as_MirrorPadOptions();
|
||||
if (mirror_pad_params != nullptr) {
|
||||
params->mode =
|
||||
mirror_pad_params->mode() == tflite::MirrorPadMode_REFLECT
|
||||
? TfLiteMirrorPaddingMode::kTfLiteMirrorPaddingReflect
|
||||
: TfLiteMirrorPaddingMode::kTfLiteMirrorPaddingSymmetric;
|
||||
}
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_UNIQUE: {
|
||||
auto params = safe_allocator.Allocate<TfLiteUniqueParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
@@ -747,17 +755,8 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_CALL_ONCE: {
|
||||
auto params = safe_allocator.Allocate<TfLiteCallOnceParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
if (const auto* call_once_params =
|
||||
op->builtin_options_as_CallOnceOptions()) {
|
||||
params->init_subgraph_index = call_once_params->init_subgraph_index();
|
||||
}
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_CONV_3D: {
|
||||
case BuiltinOperator_CONV_3D:
|
||||
case BuiltinOperator_CONV_3D_TRANSPOSE: {
|
||||
auto params = safe_allocator.Allocate<TfLiteConv3DParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
if (const auto* conv3d_params = op->builtin_options_as_Conv3DOptions()) {
|
||||
@@ -789,6 +788,73 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_MULTINOMIAL: {
|
||||
auto params = safe_allocator.Allocate<TfLiteRandomParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
if (const auto* multinomial_params =
|
||||
op->builtin_options_as_RandomOptions()) {
|
||||
params->seed = multinomial_params->seed();
|
||||
params->seed2 = multinomial_params->seed2();
|
||||
}
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_RANDOM_STANDARD_NORMAL: {
|
||||
auto params = safe_allocator.Allocate<TfLiteRandomParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
if (const auto* random_std_normal_params =
|
||||
op->builtin_options_as_RandomOptions()) {
|
||||
params->seed = random_std_normal_params->seed();
|
||||
params->seed2 = random_std_normal_params->seed2();
|
||||
}
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_BUCKETIZE: {
|
||||
auto params = safe_allocator.Allocate<TfLiteBucketizeParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
if (const auto* bucketize_params =
|
||||
op->builtin_options_as_BucketizeOptions()) {
|
||||
const flatbuffers::Vector<float>* boundaries =
|
||||
bucketize_params->boundaries();
|
||||
if (boundaries == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter,
|
||||
"boundaries array not provided for operation 'bucketize'.\n");
|
||||
return kTfLiteError;
|
||||
}
|
||||
params->num_boundaries = boundaries->size();
|
||||
if (boundaries->data() == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter,
|
||||
"boundaries.data() returned nullptr for "
|
||||
"operation 'bucketize'.\n");
|
||||
return kTfLiteError;
|
||||
}
|
||||
params->boundaries = boundaries->data();
|
||||
}
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_RANDOM_UNIFORM: {
|
||||
auto params = safe_allocator.Allocate<TfLiteRandomParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
if (const auto* random_uniform_params =
|
||||
op->builtin_options_as_RandomOptions()) {
|
||||
params->seed = random_uniform_params->seed();
|
||||
params->seed2 = random_uniform_params->seed2();
|
||||
}
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case BuiltinOperator_GELU: {
|
||||
auto params = safe_allocator.Allocate<TfLiteGeluParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
if (const auto* gelu_params = op->builtin_options_as_GeluOptions()) {
|
||||
params->approximate = gelu_params->approximate();
|
||||
}
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
// Below are the ops with no builtin_data structure.
|
||||
// TODO(aselle): Implement call in BuiltinOptions, but nullptrs are
|
||||
// ok for now, since there is no call implementation either.
|
||||
@@ -825,6 +891,7 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
|
||||
case BuiltinOperator_HASHTABLE_FIND:
|
||||
case BuiltinOperator_HASHTABLE_IMPORT:
|
||||
case BuiltinOperator_HASHTABLE_SIZE:
|
||||
case BuiltinOperator_BROADCAST_ARGS:
|
||||
return kTfLiteOk;
|
||||
case BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES:
|
||||
return kTfLiteError;
|
||||
@@ -981,6 +1048,14 @@ TfLiteStatus ParseArgMin(const Operator* op, ErrorReporter* error_reporter,
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// We have this parse function instead of directly returning kTfLiteOk from the
|
||||
// switch-case in ParseOpData because this function is used as part of the
|
||||
// selective registration for the OpResolver implementation in micro.
|
||||
TfLiteStatus ParseAssignVariable(const Operator*, ErrorReporter*,
|
||||
BuiltinDataAllocator*, void**) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// We have this parse function instead of directly returning kTfLiteOk from the
|
||||
// switch-case in ParseOpData because this function is used as part of the
|
||||
// selective registration for the OpResolver implementation in micro.
|
||||
@@ -1010,6 +1085,33 @@ TfLiteStatus ParseBatchToSpaceNd(const Operator*, ErrorReporter*,
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ParseCallOnce(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data) {
|
||||
CheckParsePointerParams(op, error_reporter, allocator, builtin_data);
|
||||
|
||||
SafeBuiltinDataAllocator safe_allocator(allocator);
|
||||
std::unique_ptr<TfLiteCallOnceParams,
|
||||
SafeBuiltinDataAllocator::BuiltinDataDeleter>
|
||||
params = safe_allocator.Allocate<TfLiteCallOnceParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
|
||||
const CallOnceOptions* schema_params =
|
||||
op->builtin_options_as_CallOnceOptions();
|
||||
|
||||
if (schema_params != nullptr) {
|
||||
params->init_subgraph_index = schema_params->init_subgraph_index();
|
||||
|
||||
} else {
|
||||
// TODO(b/157480169): We should either return kTfLiteError or fill in some
|
||||
// reasonable defaults in the params struct. We are not doing so until we
|
||||
// better undertand the ramifications of changing the legacy behavior.
|
||||
}
|
||||
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// We have this parse function instead of directly returning kTfLiteOk from the
|
||||
// switch-case in ParseOpData because this function is used as part of the
|
||||
// selective registration for the OpResolver implementation in micro.
|
||||
@@ -1372,6 +1474,30 @@ TfLiteStatus ParseHardSwish(const Operator*, ErrorReporter*,
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ParseIf(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data) {
|
||||
CheckParsePointerParams(op, error_reporter, allocator, builtin_data);
|
||||
|
||||
SafeBuiltinDataAllocator safe_allocator(allocator);
|
||||
std::unique_ptr<TfLiteIfParams, SafeBuiltinDataAllocator::BuiltinDataDeleter>
|
||||
params = safe_allocator.Allocate<TfLiteIfParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
|
||||
const IfOptions* schema_params = op->builtin_options_as_IfOptions();
|
||||
|
||||
if (schema_params != nullptr) {
|
||||
params->then_subgraph_index = schema_params->then_subgraph_index();
|
||||
params->else_subgraph_index = schema_params->else_subgraph_index();
|
||||
} else {
|
||||
// TODO(b/157480169): We should either return kTfLiteError or fill in some
|
||||
// reasonable defaults in the params struct. We are not doing so until we
|
||||
// better undertand the ramifications of changing the legacy behavior.
|
||||
}
|
||||
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ParseL2Normalization(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
@@ -1495,6 +1621,32 @@ TfLiteStatus ParseMinimum(const Operator*, ErrorReporter*,
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ParseMirrorPad(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data) {
|
||||
CheckParsePointerParams(op, error_reporter, allocator, builtin_data);
|
||||
|
||||
SafeBuiltinDataAllocator safe_allocator(allocator);
|
||||
std::unique_ptr<TfLiteMirrorPaddingParams,
|
||||
SafeBuiltinDataAllocator::BuiltinDataDeleter>
|
||||
params = safe_allocator.Allocate<TfLiteMirrorPaddingParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
|
||||
const MirrorPadOptions* schema_params =
|
||||
op->builtin_options_as_MirrorPadOptions();
|
||||
|
||||
if (schema_params != nullptr) {
|
||||
params->mode = ConvertMirrorPadding(schema_params->mode());
|
||||
} else {
|
||||
// TODO(b/157480169): We should either return kTfLiteError or fill in some
|
||||
// reasonable defaults in the params struct. We are not doing so until we
|
||||
// better undertand the ramifications of changing the legacy behavior.
|
||||
}
|
||||
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ParseMul(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data) {
|
||||
CheckParsePointerParams(op, error_reporter, allocator, builtin_data);
|
||||
@@ -1630,6 +1782,14 @@ TfLiteStatus ParseQuantize(const Operator*, ErrorReporter*,
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// We have this parse function instead of directly returning kTfLiteOk from the
|
||||
// switch-case in ParseOpData because this function is used as part of the
|
||||
// selective registration for the OpResolver implementation in micro.
|
||||
TfLiteStatus ParseReadVariable(const Operator*, ErrorReporter*,
|
||||
BuiltinDataAllocator*, void**) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ParseReducer(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data) {
|
||||
@@ -1810,6 +1970,14 @@ TfLiteStatus ParseSin(const Operator*, ErrorReporter*, BuiltinDataAllocator*,
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// We have this parse function instead of directly returning kTfLiteOk from the
|
||||
// switch-case in ParseOpData because this function is used as part of the
|
||||
// selective registration for the OpResolver implementation in micro.
|
||||
TfLiteStatus ParseSlice(const Operator*, ErrorReporter*, BuiltinDataAllocator*,
|
||||
void**) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ParseSoftmax(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data) {
|
||||
@@ -1916,6 +2084,29 @@ TfLiteStatus ParseSplitV(const Operator* op, ErrorReporter* error_reporter,
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ParseUnidirectionalSequenceLSTM(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data) {
|
||||
CheckParsePointerParams(op, error_reporter, allocator, builtin_data);
|
||||
SafeBuiltinDataAllocator safe_allocator(allocator);
|
||||
auto params =
|
||||
safe_allocator.Allocate<TfLiteUnidirectionalSequenceLSTMParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
if (const auto* seq_lstm_params =
|
||||
op->builtin_options_as_UnidirectionalSequenceLSTMOptions()) {
|
||||
params->activation =
|
||||
ConvertActivation(seq_lstm_params->fused_activation_function());
|
||||
params->cell_clip = seq_lstm_params->cell_clip();
|
||||
params->proj_clip = seq_lstm_params->proj_clip();
|
||||
params->time_major = seq_lstm_params->time_major();
|
||||
params->asymmetric_quantize_inputs =
|
||||
seq_lstm_params->asymmetric_quantize_inputs();
|
||||
}
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ParseSqueeze(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data) {
|
||||
@@ -2115,6 +2306,37 @@ TfLiteStatus ParseUnpack(const Operator* op, ErrorReporter* error_reporter,
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ParseVarHandle(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data) {
|
||||
CheckParsePointerParams(op, error_reporter, allocator, builtin_data);
|
||||
|
||||
SafeBuiltinDataAllocator safe_allocator(allocator);
|
||||
std::unique_ptr<TfLiteVarHandleParams,
|
||||
SafeBuiltinDataAllocator::BuiltinDataDeleter>
|
||||
params = safe_allocator.Allocate<TfLiteVarHandleParams>();
|
||||
TF_LITE_ENSURE(error_reporter, params != nullptr);
|
||||
|
||||
const VarHandleOptions* schema_params =
|
||||
op->builtin_options_as_VarHandleOptions();
|
||||
|
||||
if (schema_params != nullptr) {
|
||||
if (schema_params->container()) {
|
||||
params->container = schema_params->container()->c_str();
|
||||
}
|
||||
if (schema_params->shared_name()) {
|
||||
params->shared_name = schema_params->shared_name()->c_str();
|
||||
}
|
||||
} else {
|
||||
// TODO(b/157480169): We should either return kTfLiteError or fill in some
|
||||
// reasonable defaults in the params struct. We are not doing so until we
|
||||
// better undertand the ramifications of changing the legacy behavior.
|
||||
}
|
||||
|
||||
*builtin_data = params.release();
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// We have this parse function instead of directly returning kTfLiteOk from the
|
||||
// switch-case in ParseOpData because this function is used as part of the
|
||||
// selective registration for the OpResolver implementation in micro.
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
|
||||
/* Copyright 2021 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.
|
||||
@@ -84,6 +84,11 @@ TfLiteStatus ParseArgMax(const Operator* op, ErrorReporter* error_reporter,
|
||||
TfLiteStatus ParseArgMin(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseAssignVariable(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseBatchMatMul(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
@@ -93,6 +98,10 @@ TfLiteStatus ParseBatchToSpaceNd(const Operator* op,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCallOnce(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCeil(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
@@ -181,6 +190,9 @@ TfLiteStatus ParseHardSwish(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseIf(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseL2Normalization(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
@@ -226,6 +238,10 @@ TfLiteStatus ParseMaximum(const Operator* op, ErrorReporter* error_reporter,
|
||||
TfLiteStatus ParseMinimum(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseMirrorPad(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseMul(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
@@ -258,6 +274,11 @@ TfLiteStatus ParseQuantize(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseReadVariable(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseReducer(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
@@ -292,6 +313,9 @@ TfLiteStatus ParseShape(const Operator* op, ErrorReporter* error_reporter,
|
||||
TfLiteStatus ParseSin(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSlice(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSoftmax(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
@@ -346,6 +370,15 @@ TfLiteStatus ParseTransposeConv(const Operator* op,
|
||||
TfLiteStatus ParseUnpack(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseUnidirectionalSequenceLSTM(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseVarHandle(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseZerosLike(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
@@ -30,8 +30,7 @@ TfLiteStatus GetRegistrationFromOpCode(
|
||||
auto builtin_code = GetBuiltinCode(opcode);
|
||||
int version = opcode->version();
|
||||
|
||||
if (builtin_code > BuiltinOperator_MAX ||
|
||||
builtin_code < BuiltinOperator_MIN) {
|
||||
if (builtin_code > BuiltinOperator_MAX) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter,
|
||||
"Op builtin_code out of range: %d. Are you using old TFLite binary "
|
||||
@@ -15,6 +15,7 @@ limitations under the License.
|
||||
#ifndef TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_
|
||||
#define TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -36,16 +37,43 @@ class OpResolver {
|
||||
virtual const TfLiteRegistration* FindOp(const char* op,
|
||||
int version) const = 0;
|
||||
|
||||
using TfLiteDelegatePtrVector =
|
||||
std::vector<std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>>;
|
||||
// Returns optional delegates for resolving and handling ops in the flatbuffer
|
||||
// model. This may be used in addition to the standard TfLiteRegistration
|
||||
// lookup for graph resolution.
|
||||
using TfLiteDelegatePtrVector =
|
||||
std::vector<std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>>;
|
||||
// WARNING: This API is deprecated, GetDelegateCreators is preferred.
|
||||
virtual TfLiteDelegatePtrVector GetDelegates(int num_threads) const {
|
||||
return TfLiteDelegatePtrVector();
|
||||
return {};
|
||||
}
|
||||
|
||||
// Represent a function that creates a TfLite delegate instance.
|
||||
using TfLiteDelegateCreator =
|
||||
std::function<std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>(
|
||||
int /*num_threads*/)>;
|
||||
using TfLiteDelegateCreators = std::vector<TfLiteDelegateCreator>;
|
||||
// Returns a vector of delegate creators to create optional delegates for
|
||||
// resolving and handling ops in the flatbuffer model. This may be used in
|
||||
// addition to the standard TfLiteRegistration lookup for graph resolution.
|
||||
virtual TfLiteDelegateCreators GetDelegateCreators() const { return {}; }
|
||||
|
||||
virtual ~OpResolver() {}
|
||||
|
||||
private:
|
||||
/// Returns true if this OpResolver may contain any "user defined" ops.
|
||||
/// By "user defined" ops, we mean any op definitions other than those
|
||||
/// contained in tflite::ops::builtin::BuiltinOpResolver.
|
||||
///
|
||||
/// If this method returns true, it doesn't necessarily mean that the
|
||||
/// OpResolver contains a user-defined op, just that the absence of
|
||||
/// user-defined ops can't be guaranteed.
|
||||
///
|
||||
/// Note that "user-defined" ops are not the same as "custom" ops;
|
||||
/// BuiltinOpResolver may support certain "custom" ops, in addition to
|
||||
/// "builtin" ops, and may not support all of the "builtin" op enum values.
|
||||
virtual bool MayContainUserDefinedOps() const { return true; }
|
||||
|
||||
friend class OpResolverInternal;
|
||||
};
|
||||
|
||||
// Handles the logic for converting between an OperatorCode structure extracted
|
||||
@@ -0,0 +1,102 @@
|
||||
/* 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_MICROFRONTEND_LIB_BITS_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_BITS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline int CountLeadingZeros32Slow(uint64_t n) {
|
||||
int zeroes = 28;
|
||||
if (n >> 16) zeroes -= 16, n >>= 16;
|
||||
if (n >> 8) zeroes -= 8, n >>= 8;
|
||||
if (n >> 4) zeroes -= 4, n >>= 4;
|
||||
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
|
||||
}
|
||||
|
||||
static inline int CountLeadingZeros32(uint32_t n) {
|
||||
#if defined(_MSC_VER)
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (_BitScanReverse(&result, n)) {
|
||||
return 31 - result;
|
||||
}
|
||||
return 32;
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
// Handle 0 as a special case because __builtin_clz(0) is undefined.
|
||||
if (n == 0) {
|
||||
return 32;
|
||||
}
|
||||
return __builtin_clz(n);
|
||||
#else
|
||||
return CountLeadingZeros32Slow(n);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int MostSignificantBit32(uint32_t n) {
|
||||
return 32 - CountLeadingZeros32(n);
|
||||
}
|
||||
|
||||
static inline int CountLeadingZeros64Slow(uint64_t n) {
|
||||
int zeroes = 60;
|
||||
if (n >> 32) zeroes -= 32, n >>= 32;
|
||||
if (n >> 16) zeroes -= 16, n >>= 16;
|
||||
if (n >> 8) zeroes -= 8, n >>= 8;
|
||||
if (n >> 4) zeroes -= 4, n >>= 4;
|
||||
return "\4\3\2\2\1\1\1\1\0\0\0\0\0\0\0"[n] + zeroes;
|
||||
}
|
||||
|
||||
static inline int CountLeadingZeros64(uint64_t n) {
|
||||
#if defined(_MSC_VER) && defined(_M_X64)
|
||||
// MSVC does not have __builtin_clzll. Use _BitScanReverse64.
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if (_BitScanReverse64(&result, n)) {
|
||||
return 63 - result;
|
||||
}
|
||||
return 64;
|
||||
#elif defined(_MSC_VER)
|
||||
// MSVC does not have __builtin_clzll. Compose two calls to _BitScanReverse
|
||||
unsigned long result = 0; // NOLINT(runtime/int)
|
||||
if ((n >> 32) && _BitScanReverse(&result, n >> 32)) {
|
||||
return 31 - result;
|
||||
}
|
||||
if (_BitScanReverse(&result, n)) {
|
||||
return 63 - result;
|
||||
}
|
||||
return 64;
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
// Handle 0 as a special case because __builtin_clzll(0) is undefined.
|
||||
if (n == 0) {
|
||||
return 64;
|
||||
}
|
||||
return __builtin_clzll(n);
|
||||
#else
|
||||
return CountLeadingZeros64Slow(n);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int MostSignificantBit64(uint64_t n) {
|
||||
return 64 - CountLeadingZeros64(n);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_BITS_H_
|
||||
@@ -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.
|
||||
==============================================================================*/
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
void FftCompute(struct FftState* state, const int16_t* input,
|
||||
int input_scale_shift) {
|
||||
const size_t input_size = state->input_size;
|
||||
const size_t fft_size = state->fft_size;
|
||||
|
||||
int16_t* fft_input = state->input;
|
||||
// First, scale the input by the given shift.
|
||||
size_t i;
|
||||
for (i = 0; i < input_size; ++i) {
|
||||
fft_input[i] = static_cast<int16_t>(static_cast<uint16_t>(input[i])
|
||||
<< input_scale_shift);
|
||||
}
|
||||
// Zero out whatever else remains in the top part of the input.
|
||||
for (; i < fft_size; ++i) {
|
||||
fft_input[i] = 0;
|
||||
}
|
||||
|
||||
// Apply the FFT.
|
||||
kissfft_fixed16::kiss_fftr(
|
||||
reinterpret_cast<kissfft_fixed16::kiss_fftr_cfg>(state->scratch),
|
||||
state->input,
|
||||
reinterpret_cast<kissfft_fixed16::kiss_fft_cpx*>(state->output));
|
||||
}
|
||||
|
||||
void FftInit(struct FftState* state) {
|
||||
// All the initialization is done in FftPopulateState()
|
||||
}
|
||||
|
||||
void FftReset(struct FftState* state) {
|
||||
memset(state->input, 0, state->fft_size * sizeof(*state->input));
|
||||
memset(state->output, 0, (state->fft_size / 2 + 1) * sizeof(*state->output));
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* 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_MICROFRONTEND_LIB_FFT_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct complex_int16_t {
|
||||
int16_t real;
|
||||
int16_t imag;
|
||||
};
|
||||
|
||||
struct FftState {
|
||||
int16_t* input;
|
||||
struct complex_int16_t* output;
|
||||
size_t fft_size;
|
||||
size_t input_size;
|
||||
void* scratch;
|
||||
size_t scratch_size;
|
||||
};
|
||||
|
||||
void FftCompute(struct FftState* state, const int16_t* input,
|
||||
int input_scale_shift);
|
||||
|
||||
void FftInit(struct FftState* state);
|
||||
|
||||
void FftReset(struct FftState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_H_
|
||||
@@ -0,0 +1,69 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/fft_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int FftPopulateState(struct FftState* state, size_t input_size) {
|
||||
state->input_size = input_size;
|
||||
state->fft_size = 1;
|
||||
while (state->fft_size < state->input_size) {
|
||||
state->fft_size <<= 1;
|
||||
}
|
||||
|
||||
state->input = reinterpret_cast<int16_t*>(
|
||||
malloc(state->fft_size * sizeof(*state->input)));
|
||||
if (state->input == nullptr) {
|
||||
fprintf(stderr, "Failed to alloc fft input buffer\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->output = reinterpret_cast<complex_int16_t*>(
|
||||
malloc((state->fft_size / 2 + 1) * sizeof(*state->output) * 2));
|
||||
if (state->output == nullptr) {
|
||||
fprintf(stderr, "Failed to alloc fft output buffer\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Ask kissfft how much memory it wants.
|
||||
size_t scratch_size = 0;
|
||||
kissfft_fixed16::kiss_fftr_cfg kfft_cfg = kissfft_fixed16::kiss_fftr_alloc(
|
||||
state->fft_size, 0, nullptr, &scratch_size);
|
||||
if (kfft_cfg != nullptr) {
|
||||
fprintf(stderr, "Kiss memory sizing failed.\n");
|
||||
return 0;
|
||||
}
|
||||
state->scratch = malloc(scratch_size);
|
||||
if (state->scratch == nullptr) {
|
||||
fprintf(stderr, "Failed to alloc fft scratch buffer\n");
|
||||
return 0;
|
||||
}
|
||||
state->scratch_size = scratch_size;
|
||||
// Let kissfft configure the scratch space we just allocated
|
||||
kfft_cfg = kissfft_fixed16::kiss_fftr_alloc(state->fft_size, 0,
|
||||
state->scratch, &scratch_size);
|
||||
if (kfft_cfg != state->scratch) {
|
||||
fprintf(stderr, "Kiss memory preallocation strategy failed.\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void FftFreeStateContents(struct FftState* state) {
|
||||
free(state->input);
|
||||
free(state->output);
|
||||
free(state->scratch);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* 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_MICROFRONTEND_LIB_FFT_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Prepares and FFT for the given input size.
|
||||
int FftPopulateState(struct FftState* state, size_t input_size);
|
||||
|
||||
// Frees any allocated buffers.
|
||||
void FftFreeStateContents(struct FftState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FFT_UTIL_H_
|
||||
@@ -0,0 +1,134 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/filterbank.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
|
||||
|
||||
void FilterbankConvertFftComplexToEnergy(struct FilterbankState* state,
|
||||
struct complex_int16_t* fft_output,
|
||||
int32_t* energy) {
|
||||
const int end_index = state->end_index;
|
||||
int i;
|
||||
energy += state->start_index;
|
||||
fft_output += state->start_index;
|
||||
for (i = state->start_index; i < end_index; ++i) {
|
||||
const int32_t real = fft_output->real;
|
||||
const int32_t imag = fft_output->imag;
|
||||
fft_output++;
|
||||
const uint32_t mag_squared = (real * real) + (imag * imag);
|
||||
*energy++ = mag_squared;
|
||||
}
|
||||
}
|
||||
|
||||
void FilterbankAccumulateChannels(struct FilterbankState* state,
|
||||
const int32_t* energy) {
|
||||
uint64_t* work = state->work;
|
||||
uint64_t weight_accumulator = 0;
|
||||
uint64_t unweight_accumulator = 0;
|
||||
|
||||
const int16_t* channel_frequency_starts = state->channel_frequency_starts;
|
||||
const int16_t* channel_weight_starts = state->channel_weight_starts;
|
||||
const int16_t* channel_widths = state->channel_widths;
|
||||
|
||||
int num_channels_plus_1 = state->num_channels + 1;
|
||||
int i;
|
||||
for (i = 0; i < num_channels_plus_1; ++i) {
|
||||
const int32_t* magnitudes = energy + *channel_frequency_starts++;
|
||||
const int16_t* weights = state->weights + *channel_weight_starts;
|
||||
const int16_t* unweights = state->unweights + *channel_weight_starts++;
|
||||
const int width = *channel_widths++;
|
||||
int j;
|
||||
for (j = 0; j < width; ++j) {
|
||||
weight_accumulator += *weights++ * ((uint64_t)*magnitudes);
|
||||
unweight_accumulator += *unweights++ * ((uint64_t)*magnitudes);
|
||||
++magnitudes;
|
||||
}
|
||||
*work++ = weight_accumulator;
|
||||
weight_accumulator = unweight_accumulator;
|
||||
unweight_accumulator = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t Sqrt32(uint32_t num) {
|
||||
if (num == 0) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t res = 0;
|
||||
int max_bit_number = 32 - MostSignificantBit32(num);
|
||||
max_bit_number |= 1;
|
||||
uint32_t bit = 1U << (31 - max_bit_number);
|
||||
int iterations = (31 - max_bit_number) / 2 + 1;
|
||||
while (iterations--) {
|
||||
if (num >= res + bit) {
|
||||
num -= res + bit;
|
||||
res = (res >> 1U) + bit;
|
||||
} else {
|
||||
res >>= 1U;
|
||||
}
|
||||
bit >>= 2U;
|
||||
}
|
||||
// Do rounding - if we have the bits.
|
||||
if (num > res && res != 0xFFFF) {
|
||||
++res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static uint32_t Sqrt64(uint64_t num) {
|
||||
// Take a shortcut and just use 32 bit operations if the upper word is all
|
||||
// clear. This will cause a slight off by one issue for numbers close to 2^32,
|
||||
// but it probably isn't going to matter (and gives us a big performance win).
|
||||
if ((num >> 32) == 0) {
|
||||
return Sqrt32((uint32_t)num);
|
||||
}
|
||||
uint64_t res = 0;
|
||||
int max_bit_number = 64 - MostSignificantBit64(num);
|
||||
max_bit_number |= 1;
|
||||
uint64_t bit = 1ULL << (63 - max_bit_number);
|
||||
int iterations = (63 - max_bit_number) / 2 + 1;
|
||||
while (iterations--) {
|
||||
if (num >= res + bit) {
|
||||
num -= res + bit;
|
||||
res = (res >> 1U) + bit;
|
||||
} else {
|
||||
res >>= 1U;
|
||||
}
|
||||
bit >>= 2U;
|
||||
}
|
||||
// Do rounding - if we have the bits.
|
||||
if (num > res && res != 0xFFFFFFFFLL) {
|
||||
++res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t* FilterbankSqrt(struct FilterbankState* state, int scale_down_shift) {
|
||||
const int num_channels = state->num_channels;
|
||||
const uint64_t* work = state->work + 1;
|
||||
// Reuse the work buffer since we're fine clobbering it at this point to hold
|
||||
// the output.
|
||||
uint32_t* output = (uint32_t*)state->work;
|
||||
int i;
|
||||
for (i = 0; i < num_channels; ++i) {
|
||||
*output++ = Sqrt64(*work++) >> scale_down_shift;
|
||||
}
|
||||
return (uint32_t*)state->work;
|
||||
}
|
||||
|
||||
void FilterbankReset(struct FilterbankState* state) {
|
||||
memset(state->work, 0, (state->num_channels + 1) * sizeof(*state->work));
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/* 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_MICROFRONTEND_LIB_FILTERBANK_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
|
||||
|
||||
#define kFilterbankBits 12
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct FilterbankState {
|
||||
int num_channels;
|
||||
int start_index;
|
||||
int end_index;
|
||||
int16_t* channel_frequency_starts;
|
||||
int16_t* channel_weight_starts;
|
||||
int16_t* channel_widths;
|
||||
int16_t* weights;
|
||||
int16_t* unweights;
|
||||
uint64_t* work;
|
||||
};
|
||||
|
||||
// Converts the relevant complex values of an FFT output into energy (the
|
||||
// square magnitude).
|
||||
void FilterbankConvertFftComplexToEnergy(struct FilterbankState* state,
|
||||
struct complex_int16_t* fft_output,
|
||||
int32_t* energy);
|
||||
|
||||
// Computes the mel-scale filterbank on the given energy array. Output is cached
|
||||
// internally - to fetch it, you need to call FilterbankSqrt.
|
||||
void FilterbankAccumulateChannels(struct FilterbankState* state,
|
||||
const int32_t* energy);
|
||||
|
||||
// Applies an integer square root to the 64 bit intermediate values of the
|
||||
// filterbank, and returns a pointer to them. Memory will be invalidated the
|
||||
// next time FilterbankAccumulateChannels is called.
|
||||
uint32_t* FilterbankSqrt(struct FilterbankState* state, int scale_down_shift);
|
||||
|
||||
void FilterbankReset(struct FilterbankState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_H_
|
||||
@@ -0,0 +1,220 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define kFilterbankIndexAlignment 4
|
||||
#define kFilterbankChannelBlockSize 4
|
||||
|
||||
void FilterbankFillConfigWithDefaults(struct FilterbankConfig* config) {
|
||||
config->num_channels = 32;
|
||||
config->lower_band_limit = 125.0f;
|
||||
config->upper_band_limit = 7500.0f;
|
||||
config->output_scale_shift = 7;
|
||||
}
|
||||
|
||||
static float FreqToMel(float freq) { return 1127.0 * log1p(freq / 700.0); }
|
||||
|
||||
static void CalculateCenterFrequencies(const int num_channels,
|
||||
const float lower_frequency_limit,
|
||||
const float upper_frequency_limit,
|
||||
float* center_frequencies) {
|
||||
assert(lower_frequency_limit >= 0.0f);
|
||||
assert(upper_frequency_limit > lower_frequency_limit);
|
||||
|
||||
const float mel_low = FreqToMel(lower_frequency_limit);
|
||||
const float mel_hi = FreqToMel(upper_frequency_limit);
|
||||
const float mel_span = mel_hi - mel_low;
|
||||
const float mel_spacing = mel_span / ((float)num_channels);
|
||||
int i;
|
||||
for (i = 0; i < num_channels; ++i) {
|
||||
center_frequencies[i] = mel_low + (mel_spacing * (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
static void QuantizeFilterbankWeights(const float float_weight, int16_t* weight,
|
||||
int16_t* unweight) {
|
||||
*weight = floor(float_weight * (1 << kFilterbankBits) + 0.5);
|
||||
*unweight = floor((1.0 - float_weight) * (1 << kFilterbankBits) + 0.5);
|
||||
}
|
||||
|
||||
int FilterbankPopulateState(const struct FilterbankConfig* config,
|
||||
struct FilterbankState* state, int sample_rate,
|
||||
int spectrum_size) {
|
||||
state->num_channels = config->num_channels;
|
||||
const int num_channels_plus_1 = config->num_channels + 1;
|
||||
|
||||
// How should we align things to index counts given the byte alignment?
|
||||
const int index_alignment =
|
||||
(kFilterbankIndexAlignment < sizeof(int16_t)
|
||||
? 1
|
||||
: kFilterbankIndexAlignment / sizeof(int16_t));
|
||||
|
||||
state->channel_frequency_starts =
|
||||
malloc(num_channels_plus_1 * sizeof(*state->channel_frequency_starts));
|
||||
state->channel_weight_starts =
|
||||
malloc(num_channels_plus_1 * sizeof(*state->channel_weight_starts));
|
||||
state->channel_widths =
|
||||
malloc(num_channels_plus_1 * sizeof(*state->channel_widths));
|
||||
state->work = malloc(num_channels_plus_1 * sizeof(*state->work));
|
||||
|
||||
float* center_mel_freqs =
|
||||
malloc(num_channels_plus_1 * sizeof(*center_mel_freqs));
|
||||
int16_t* actual_channel_starts =
|
||||
malloc(num_channels_plus_1 * sizeof(*actual_channel_starts));
|
||||
int16_t* actual_channel_widths =
|
||||
malloc(num_channels_plus_1 * sizeof(*actual_channel_widths));
|
||||
|
||||
if (state->channel_frequency_starts == NULL ||
|
||||
state->channel_weight_starts == NULL || state->channel_widths == NULL ||
|
||||
center_mel_freqs == NULL || actual_channel_starts == NULL ||
|
||||
actual_channel_widths == NULL) {
|
||||
free(center_mel_freqs);
|
||||
free(actual_channel_starts);
|
||||
free(actual_channel_widths);
|
||||
fprintf(stderr, "Failed to allocate channel buffers\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
CalculateCenterFrequencies(num_channels_plus_1, config->lower_band_limit,
|
||||
config->upper_band_limit, center_mel_freqs);
|
||||
|
||||
// Always exclude DC.
|
||||
const float hz_per_sbin = 0.5 * sample_rate / ((float)spectrum_size - 1);
|
||||
state->start_index = 1.5 + config->lower_band_limit / hz_per_sbin;
|
||||
state->end_index = 0; // Initialized to zero here, but actually set below.
|
||||
|
||||
// For each channel, we need to figure out what frequencies belong to it, and
|
||||
// how much padding we need to add so that we can efficiently multiply the
|
||||
// weights and unweights for accumulation. To simplify the multiplication
|
||||
// logic, all channels will have some multiplication to do (even if there are
|
||||
// no frequencies that accumulate to that channel) - they will be directed to
|
||||
// a set of zero weights.
|
||||
int chan_freq_index_start = state->start_index;
|
||||
int weight_index_start = 0;
|
||||
int needs_zeros = 0;
|
||||
|
||||
int chan;
|
||||
for (chan = 0; chan < num_channels_plus_1; ++chan) {
|
||||
// Keep jumping frequencies until we overshoot the bound on this channel.
|
||||
int freq_index = chan_freq_index_start;
|
||||
while (FreqToMel((freq_index)*hz_per_sbin) <= center_mel_freqs[chan]) {
|
||||
++freq_index;
|
||||
}
|
||||
|
||||
const int width = freq_index - chan_freq_index_start;
|
||||
actual_channel_starts[chan] = chan_freq_index_start;
|
||||
actual_channel_widths[chan] = width;
|
||||
|
||||
if (width == 0) {
|
||||
// This channel doesn't actually get anything from the frequencies, it's
|
||||
// always zero. We need then to insert some 'zero' weights into the
|
||||
// output, and just redirect this channel to do a single multiplication at
|
||||
// this point. For simplicity, the zeros are placed at the beginning of
|
||||
// the weights arrays, so we have to go and update all the other
|
||||
// weight_starts to reflect this shift (but only once).
|
||||
state->channel_frequency_starts[chan] = 0;
|
||||
state->channel_weight_starts[chan] = 0;
|
||||
state->channel_widths[chan] = kFilterbankChannelBlockSize;
|
||||
if (!needs_zeros) {
|
||||
needs_zeros = 1;
|
||||
int j;
|
||||
for (j = 0; j < chan; ++j) {
|
||||
state->channel_weight_starts[j] += kFilterbankChannelBlockSize;
|
||||
}
|
||||
weight_index_start += kFilterbankChannelBlockSize;
|
||||
}
|
||||
} else {
|
||||
// How far back do we need to go to ensure that we have the proper
|
||||
// alignment?
|
||||
const int aligned_start =
|
||||
(chan_freq_index_start / index_alignment) * index_alignment;
|
||||
const int aligned_width = (chan_freq_index_start - aligned_start + width);
|
||||
const int padded_width =
|
||||
(((aligned_width - 1) / kFilterbankChannelBlockSize) + 1) *
|
||||
kFilterbankChannelBlockSize;
|
||||
|
||||
state->channel_frequency_starts[chan] = aligned_start;
|
||||
state->channel_weight_starts[chan] = weight_index_start;
|
||||
state->channel_widths[chan] = padded_width;
|
||||
weight_index_start += padded_width;
|
||||
}
|
||||
chan_freq_index_start = freq_index;
|
||||
}
|
||||
|
||||
// Allocate the two arrays to store the weights - weight_index_start contains
|
||||
// the index of what would be the next set of weights that we would need to
|
||||
// add, so that's how many weights we need to allocate.
|
||||
state->weights = calloc(weight_index_start, sizeof(*state->weights));
|
||||
state->unweights = calloc(weight_index_start, sizeof(*state->unweights));
|
||||
|
||||
// If the alloc failed, we also need to nuke the arrays.
|
||||
if (state->weights == NULL || state->unweights == NULL) {
|
||||
free(center_mel_freqs);
|
||||
free(actual_channel_starts);
|
||||
free(actual_channel_widths);
|
||||
fprintf(stderr, "Failed to allocate weights or unweights\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Next pass, compute all the weights. Since everything has been memset to
|
||||
// zero, we only need to fill in the weights that correspond to some frequency
|
||||
// for a channel.
|
||||
const float mel_low = FreqToMel(config->lower_band_limit);
|
||||
for (chan = 0; chan < num_channels_plus_1; ++chan) {
|
||||
int frequency = actual_channel_starts[chan];
|
||||
const int num_frequencies = actual_channel_widths[chan];
|
||||
const int frequency_offset =
|
||||
frequency - state->channel_frequency_starts[chan];
|
||||
const int weight_start = state->channel_weight_starts[chan];
|
||||
const float denom_val = (chan == 0) ? mel_low : center_mel_freqs[chan - 1];
|
||||
|
||||
int j;
|
||||
for (j = 0; j < num_frequencies; ++j, ++frequency) {
|
||||
const float weight =
|
||||
(center_mel_freqs[chan] - FreqToMel(frequency * hz_per_sbin)) /
|
||||
(center_mel_freqs[chan] - denom_val);
|
||||
|
||||
// Make the float into an integer for the weights (and unweights).
|
||||
const int weight_index = weight_start + frequency_offset + j;
|
||||
QuantizeFilterbankWeights(weight, state->weights + weight_index,
|
||||
state->unweights + weight_index);
|
||||
}
|
||||
if (frequency > state->end_index) {
|
||||
state->end_index = frequency;
|
||||
}
|
||||
}
|
||||
|
||||
free(center_mel_freqs);
|
||||
free(actual_channel_starts);
|
||||
free(actual_channel_widths);
|
||||
if (state->end_index >= spectrum_size) {
|
||||
fprintf(stderr, "Filterbank end_index is above spectrum size.\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void FilterbankFreeStateContents(struct FilterbankState* state) {
|
||||
free(state->channel_frequency_starts);
|
||||
free(state->channel_weight_starts);
|
||||
free(state->channel_widths);
|
||||
free(state->weights);
|
||||
free(state->unweights);
|
||||
free(state->work);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* 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_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct FilterbankConfig {
|
||||
// number of frequency channel buckets for filterbank
|
||||
int num_channels;
|
||||
// maximum frequency to include
|
||||
float upper_band_limit;
|
||||
// minimum frequency to include
|
||||
float lower_band_limit;
|
||||
// unused
|
||||
int output_scale_shift;
|
||||
};
|
||||
|
||||
// Fills the frontendConfig with "sane" defaults.
|
||||
void FilterbankFillConfigWithDefaults(struct FilterbankConfig* config);
|
||||
|
||||
// Allocates any buffers.
|
||||
int FilterbankPopulateState(const struct FilterbankConfig* config,
|
||||
struct FilterbankState* state, int sample_rate,
|
||||
int spectrum_size);
|
||||
|
||||
// Frees any allocated buffers.
|
||||
void FilterbankFreeStateContents(struct FilterbankState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FILTERBANK_UTIL_H_
|
||||
@@ -0,0 +1,72 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
|
||||
|
||||
struct FrontendOutput FrontendProcessSamples(struct FrontendState* state,
|
||||
const int16_t* samples,
|
||||
size_t num_samples,
|
||||
size_t* num_samples_read) {
|
||||
struct FrontendOutput output;
|
||||
output.values = NULL;
|
||||
output.size = 0;
|
||||
|
||||
// Try to apply the window - if it fails, return and wait for more data.
|
||||
if (!WindowProcessSamples(&state->window, samples, num_samples,
|
||||
num_samples_read)) {
|
||||
return output;
|
||||
}
|
||||
|
||||
// Apply the FFT to the window's output (and scale it so that the fixed point
|
||||
// FFT can have as much resolution as possible).
|
||||
int input_shift =
|
||||
15 - MostSignificantBit32(state->window.max_abs_output_value);
|
||||
FftCompute(&state->fft, state->window.output, input_shift);
|
||||
|
||||
// We can re-ruse the fft's output buffer to hold the energy.
|
||||
int32_t* energy = (int32_t*)state->fft.output;
|
||||
|
||||
FilterbankConvertFftComplexToEnergy(&state->filterbank, state->fft.output,
|
||||
energy);
|
||||
|
||||
FilterbankAccumulateChannels(&state->filterbank, energy);
|
||||
uint32_t* scaled_filterbank = FilterbankSqrt(&state->filterbank, input_shift);
|
||||
|
||||
// Apply noise reduction.
|
||||
NoiseReductionApply(&state->noise_reduction, scaled_filterbank);
|
||||
|
||||
if (state->pcan_gain_control.enable_pcan) {
|
||||
PcanGainControlApply(&state->pcan_gain_control, scaled_filterbank);
|
||||
}
|
||||
|
||||
// Apply the log and scale.
|
||||
int correction_bits =
|
||||
MostSignificantBit32(state->fft.fft_size) - 1 - (kFilterbankBits / 2);
|
||||
uint16_t* logged_filterbank =
|
||||
LogScaleApply(&state->log_scale, scaled_filterbank,
|
||||
state->filterbank.num_channels, correction_bits);
|
||||
|
||||
output.size = state->filterbank.num_channels;
|
||||
output.values = logged_filterbank;
|
||||
return output;
|
||||
}
|
||||
|
||||
void FrontendReset(struct FrontendState* state) {
|
||||
WindowReset(&state->window);
|
||||
FftReset(&state->fft);
|
||||
FilterbankReset(&state->filterbank);
|
||||
NoiseReductionReset(&state->noise_reduction);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/* 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_MICROFRONTEND_LIB_FRONTEND_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/fft.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/window.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct FrontendState {
|
||||
struct WindowState window;
|
||||
struct FftState fft;
|
||||
struct FilterbankState filterbank;
|
||||
struct NoiseReductionState noise_reduction;
|
||||
struct PcanGainControlState pcan_gain_control;
|
||||
struct LogScaleState log_scale;
|
||||
};
|
||||
|
||||
struct FrontendOutput {
|
||||
const uint16_t* values;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
// Main entry point to processing frontend samples. Updates num_samples_read to
|
||||
// contain the number of samples that have been consumed from the input array.
|
||||
// Returns a struct containing the generated output. If not enough samples were
|
||||
// added to generate a feature vector, the returned size will be 0 and the
|
||||
// values pointer will be NULL. Note that the output pointer will be invalidated
|
||||
// as soon as FrontendProcessSamples is called again, so copy the contents
|
||||
// elsewhere if you need to use them later.
|
||||
struct FrontendOutput FrontendProcessSamples(struct FrontendState* state,
|
||||
const int16_t* samples,
|
||||
size_t num_samples,
|
||||
size_t* num_samples_read);
|
||||
|
||||
void FrontendReset(struct FrontendState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_H_
|
||||
@@ -0,0 +1,85 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/frontend_util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
|
||||
|
||||
void FrontendFillConfigWithDefaults(struct FrontendConfig* config) {
|
||||
WindowFillConfigWithDefaults(&config->window);
|
||||
FilterbankFillConfigWithDefaults(&config->filterbank);
|
||||
NoiseReductionFillConfigWithDefaults(&config->noise_reduction);
|
||||
PcanGainControlFillConfigWithDefaults(&config->pcan_gain_control);
|
||||
LogScaleFillConfigWithDefaults(&config->log_scale);
|
||||
}
|
||||
|
||||
int FrontendPopulateState(const struct FrontendConfig* config,
|
||||
struct FrontendState* state, int sample_rate) {
|
||||
memset(state, 0, sizeof(*state));
|
||||
|
||||
if (!WindowPopulateState(&config->window, &state->window, sample_rate)) {
|
||||
fprintf(stderr, "Failed to populate window state\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!FftPopulateState(&state->fft, state->window.size)) {
|
||||
fprintf(stderr, "Failed to populate fft state\n");
|
||||
return 0;
|
||||
}
|
||||
FftInit(&state->fft);
|
||||
|
||||
if (!FilterbankPopulateState(&config->filterbank, &state->filterbank,
|
||||
sample_rate, state->fft.fft_size / 2 + 1)) {
|
||||
fprintf(stderr, "Failed to populate filterbank state\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!NoiseReductionPopulateState(&config->noise_reduction,
|
||||
&state->noise_reduction,
|
||||
state->filterbank.num_channels)) {
|
||||
fprintf(stderr, "Failed to populate noise reduction state\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int input_correction_bits =
|
||||
MostSignificantBit32(state->fft.fft_size) - 1 - (kFilterbankBits / 2);
|
||||
if (!PcanGainControlPopulateState(
|
||||
&config->pcan_gain_control, &state->pcan_gain_control,
|
||||
state->noise_reduction.estimate, state->filterbank.num_channels,
|
||||
state->noise_reduction.smoothing_bits, input_correction_bits)) {
|
||||
fprintf(stderr, "Failed to populate pcan gain control state\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!LogScalePopulateState(&config->log_scale, &state->log_scale)) {
|
||||
fprintf(stderr, "Failed to populate log scale state\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
FrontendReset(state);
|
||||
|
||||
// All good, return a true value.
|
||||
return 1;
|
||||
}
|
||||
|
||||
void FrontendFreeStateContents(struct FrontendState* state) {
|
||||
WindowFreeStateContents(&state->window);
|
||||
FftFreeStateContents(&state->fft);
|
||||
FilterbankFreeStateContents(&state->filterbank);
|
||||
NoiseReductionFreeStateContents(&state->noise_reduction);
|
||||
PcanGainControlFreeStateContents(&state->pcan_gain_control);
|
||||
}
|
||||
@@ -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_MICROFRONTEND_LIB_FRONTEND_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/fft_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/filterbank_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/frontend.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/window_util.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct FrontendConfig {
|
||||
struct WindowConfig window;
|
||||
struct FilterbankConfig filterbank;
|
||||
struct NoiseReductionConfig noise_reduction;
|
||||
struct PcanGainControlConfig pcan_gain_control;
|
||||
struct LogScaleConfig log_scale;
|
||||
};
|
||||
|
||||
// Fills the frontendConfig with "sane" defaults.
|
||||
void FrontendFillConfigWithDefaults(struct FrontendConfig* config);
|
||||
|
||||
// Allocates any buffers.
|
||||
int FrontendPopulateState(const struct FrontendConfig* config,
|
||||
struct FrontendState* state, int sample_rate);
|
||||
|
||||
// Frees any allocated buffers.
|
||||
void FrontendFreeStateContents(struct FrontendState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_FRONTEND_UTIL_H_
|
||||
@@ -0,0 +1,48 @@
|
||||
/* 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_MICROFRONTEND_LIB_KISS_FFT_COMMON_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_COMMON_H_
|
||||
|
||||
// This header file should be included in all variants of kiss_fft_$type.{h,cc}
|
||||
// so that their sub-included source files do not mistakenly wrap libc header
|
||||
// files within their kissfft_$type namespaces.
|
||||
// E.g, This header avoids kissfft_int16.h containing:
|
||||
// namespace kiss_fft_int16 {
|
||||
// #include "kiss_fft.h"
|
||||
// }
|
||||
// where kiss_fft_.h contains:
|
||||
// #include <math.h>
|
||||
//
|
||||
// TRICK: By including the following header files here, their preprocessor
|
||||
// header guards prevent them being re-defined inside of the kiss_fft_$type
|
||||
// namespaces declared within the kiss_fft_$type.{h,cc} sources.
|
||||
// Note that the original kiss_fft*.h files are untouched since they
|
||||
// may be used in libraries that include them directly.
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_SIMD
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_COMMON_H_
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h"
|
||||
|
||||
#define FIXED_POINT 16
|
||||
namespace kissfft_fixed16 {
|
||||
#include "kiss_fft.c"
|
||||
#include "tools/kiss_fftr.c"
|
||||
} // namespace kissfft_fixed16
|
||||
#undef FIXED_POINT
|
||||
@@ -0,0 +1,34 @@
|
||||
/* 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_MICROFRONTEND_LIB_KISS_FFT_INT16_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_INT16_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h"
|
||||
|
||||
// Wrap 16-bit kiss fft in its own namespace. Enables us to link an application
|
||||
// with different kiss fft resultions (16/32 bit interger, float, double)
|
||||
// without getting a linker error.
|
||||
#define FIXED_POINT 16
|
||||
namespace kissfft_fixed16 {
|
||||
#include "kiss_fft.h"
|
||||
#include "tools/kiss_fftr.h"
|
||||
} // namespace kissfft_fixed16
|
||||
#undef FIXED_POINT
|
||||
#undef kiss_fft_scalar
|
||||
#undef KISS_FFT_H
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_KISS_FFT_INT16_H_
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/log_lut.h"
|
||||
const uint16_t kLogLut[]
|
||||
#ifndef _MSC_VER
|
||||
__attribute__((aligned(4)))
|
||||
#endif // _MSV_VER
|
||||
= {0, 224, 442, 654, 861, 1063, 1259, 1450, 1636, 1817, 1992, 2163,
|
||||
2329, 2490, 2646, 2797, 2944, 3087, 3224, 3358, 3487, 3611, 3732, 3848,
|
||||
3960, 4068, 4172, 4272, 4368, 4460, 4549, 4633, 4714, 4791, 4864, 4934,
|
||||
5001, 5063, 5123, 5178, 5231, 5280, 5326, 5368, 5408, 5444, 5477, 5507,
|
||||
5533, 5557, 5578, 5595, 5610, 5622, 5631, 5637, 5640, 5641, 5638, 5633,
|
||||
5626, 5615, 5602, 5586, 5568, 5547, 5524, 5498, 5470, 5439, 5406, 5370,
|
||||
5332, 5291, 5249, 5203, 5156, 5106, 5054, 5000, 4944, 4885, 4825, 4762,
|
||||
4697, 4630, 4561, 4490, 4416, 4341, 4264, 4184, 4103, 4020, 3935, 3848,
|
||||
3759, 3668, 3575, 3481, 3384, 3286, 3186, 3084, 2981, 2875, 2768, 2659,
|
||||
2549, 2437, 2323, 2207, 2090, 1971, 1851, 1729, 1605, 1480, 1353, 1224,
|
||||
1094, 963, 830, 695, 559, 421, 282, 142, 0, 0};
|
||||
@@ -0,0 +1,40 @@
|
||||
/* 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_MICROFRONTEND_LIB_LOG_LUT_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_LUT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Number of segments in the log lookup table. The table will be kLogSegments+1
|
||||
// in length (with some padding).
|
||||
#define kLogSegments 128
|
||||
#define kLogSegmentsLog2 7
|
||||
|
||||
// Scale used by lookup table.
|
||||
#define kLogScale 65536
|
||||
#define kLogScaleLog2 16
|
||||
#define kLogCoeff 45426
|
||||
|
||||
extern const uint16_t kLogLut[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_LUT_H_
|
||||
@@ -0,0 +1,83 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/log_scale.h"
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/log_lut.h"
|
||||
|
||||
#define kuint16max 0x0000FFFF
|
||||
|
||||
// The following functions implement integer logarithms of various sizes. The
|
||||
// approximation is calculated according to method described in
|
||||
// www.inti.gob.ar/electronicaeinformatica/instrumentacion/utic/
|
||||
// publicaciones/SPL2007/Log10-spl07.pdf
|
||||
// It first calculates log2 of the input and then converts it to natural
|
||||
// logarithm.
|
||||
|
||||
static uint32_t Log2FractionPart(const uint32_t x, const uint32_t log2x) {
|
||||
// Part 1
|
||||
int32_t frac = x - (1LL << log2x);
|
||||
if (log2x < kLogScaleLog2) {
|
||||
frac <<= kLogScaleLog2 - log2x;
|
||||
} else {
|
||||
frac >>= log2x - kLogScaleLog2;
|
||||
}
|
||||
// Part 2
|
||||
const uint32_t base_seg = frac >> (kLogScaleLog2 - kLogSegmentsLog2);
|
||||
const uint32_t seg_unit =
|
||||
(((uint32_t)1) << kLogScaleLog2) >> kLogSegmentsLog2;
|
||||
|
||||
const int32_t c0 = kLogLut[base_seg];
|
||||
const int32_t c1 = kLogLut[base_seg + 1];
|
||||
const int32_t seg_base = seg_unit * base_seg;
|
||||
const int32_t rel_pos = ((c1 - c0) * (frac - seg_base)) >> kLogScaleLog2;
|
||||
return frac + c0 + rel_pos;
|
||||
}
|
||||
|
||||
static uint32_t Log(const uint32_t x, const uint32_t scale_shift) {
|
||||
const uint32_t integer = MostSignificantBit32(x) - 1;
|
||||
const uint32_t fraction = Log2FractionPart(x, integer);
|
||||
const uint32_t log2 = (integer << kLogScaleLog2) + fraction;
|
||||
const uint32_t round = kLogScale / 2;
|
||||
const uint32_t loge = (((uint64_t)kLogCoeff) * log2 + round) >> kLogScaleLog2;
|
||||
// Finally scale to our output scale
|
||||
const uint32_t loge_scaled = ((loge << scale_shift) + round) >> kLogScaleLog2;
|
||||
return loge_scaled;
|
||||
}
|
||||
|
||||
uint16_t* LogScaleApply(struct LogScaleState* state, uint32_t* signal,
|
||||
int signal_size, int correction_bits) {
|
||||
const int scale_shift = state->scale_shift;
|
||||
uint16_t* output = (uint16_t*)signal;
|
||||
uint16_t* ret = output;
|
||||
int i;
|
||||
for (i = 0; i < signal_size; ++i) {
|
||||
uint32_t value = *signal++;
|
||||
if (state->enable_log) {
|
||||
if (correction_bits < 0) {
|
||||
value >>= -correction_bits;
|
||||
} else {
|
||||
value <<= correction_bits;
|
||||
}
|
||||
if (value > 1) {
|
||||
value = Log(value, scale_shift);
|
||||
} else {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
*output++ = (value < kuint16max) ? value : kuint16max;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* 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_MICROFRONTEND_LIB_LOG_SCALE_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct LogScaleState {
|
||||
int enable_log;
|
||||
int scale_shift;
|
||||
};
|
||||
|
||||
// Applies a fixed point logarithm to the signal and converts it to 16 bit. Note
|
||||
// that the signal array will be modified.
|
||||
uint16_t* LogScaleApply(struct LogScaleState* state, uint32_t* signal,
|
||||
int signal_size, int correction_bits);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_H_
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
|
||||
/* 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.
|
||||
@@ -12,11 +12,16 @@ 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 "tensorflow/lite/experimental/microfrontend/lib/log_scale_util.h"
|
||||
|
||||
#ifndef TENSORFLOW_LITE_MICRO_BENCHMARKS_KEYWORD_SCRAMBLED_MODEL_DATA_H_
|
||||
#define TENSORFLOW_LITE_MICRO_BENCHMARKS_KEYWORD_SCRAMBLED_MODEL_DATA_H_
|
||||
void LogScaleFillConfigWithDefaults(struct LogScaleConfig* config) {
|
||||
config->enable_log = 1;
|
||||
config->scale_shift = 6;
|
||||
}
|
||||
|
||||
extern const unsigned char g_keyword_scrambled_model_data[];
|
||||
extern const unsigned int g_keyword_scrambled_model_data_length;
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_BENCHMARKS_KEYWORD_SCRAMBLED_MODEL_DATA_H_
|
||||
int LogScalePopulateState(const struct LogScaleConfig* config,
|
||||
struct LogScaleState* state) {
|
||||
state->enable_log = config->enable_log;
|
||||
state->scale_shift = config->scale_shift;
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/* 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_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/log_scale.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct LogScaleConfig {
|
||||
// set to false (0) to disable this module
|
||||
int enable_log;
|
||||
// scale results by 2^(scale_shift)
|
||||
int scale_shift;
|
||||
};
|
||||
|
||||
// Populates the LogScaleConfig with "sane" default values.
|
||||
void LogScaleFillConfigWithDefaults(struct LogScaleConfig* config);
|
||||
|
||||
// Allocates any buffers.
|
||||
int LogScalePopulateState(const struct LogScaleConfig* config,
|
||||
struct LogScaleState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_LOG_SCALE_UTIL_H_
|
||||
@@ -0,0 +1,51 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void NoiseReductionApply(struct NoiseReductionState* state, uint32_t* signal) {
|
||||
int i;
|
||||
for (i = 0; i < state->num_channels; ++i) {
|
||||
const uint32_t smoothing =
|
||||
((i & 1) == 0) ? state->even_smoothing : state->odd_smoothing;
|
||||
const uint32_t one_minus_smoothing = (1 << kNoiseReductionBits) - smoothing;
|
||||
|
||||
// Update the estimate of the noise.
|
||||
const uint32_t signal_scaled_up = signal[i] << state->smoothing_bits;
|
||||
uint32_t estimate =
|
||||
(((uint64_t)signal_scaled_up * smoothing) +
|
||||
((uint64_t)state->estimate[i] * one_minus_smoothing)) >>
|
||||
kNoiseReductionBits;
|
||||
state->estimate[i] = estimate;
|
||||
|
||||
// Make sure that we can't get a negative value for the signal - estimate.
|
||||
if (estimate > signal_scaled_up) {
|
||||
estimate = signal_scaled_up;
|
||||
}
|
||||
|
||||
const uint32_t floor =
|
||||
((uint64_t)signal[i] * state->min_signal_remaining) >>
|
||||
kNoiseReductionBits;
|
||||
const uint32_t subtracted =
|
||||
(signal_scaled_up - estimate) >> state->smoothing_bits;
|
||||
const uint32_t output = subtracted > floor ? subtracted : floor;
|
||||
signal[i] = output;
|
||||
}
|
||||
}
|
||||
|
||||
void NoiseReductionReset(struct NoiseReductionState* state) {
|
||||
memset(state->estimate, 0, sizeof(*state->estimate) * state->num_channels);
|
||||
}
|
||||
@@ -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_MICROFRONTEND_LIB_NOISE_REDUCTION_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_H_
|
||||
|
||||
#define kNoiseReductionBits 14
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct NoiseReductionState {
|
||||
int smoothing_bits;
|
||||
uint16_t even_smoothing;
|
||||
uint16_t odd_smoothing;
|
||||
uint16_t min_signal_remaining;
|
||||
int num_channels;
|
||||
uint32_t* estimate;
|
||||
};
|
||||
|
||||
// Removes stationary noise from each channel of the signal using a low pass
|
||||
// filter.
|
||||
void NoiseReductionApply(struct NoiseReductionState* state, uint32_t* signal);
|
||||
|
||||
void NoiseReductionReset(struct NoiseReductionState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_H_
|
||||
@@ -0,0 +1,45 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/noise_reduction_util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void NoiseReductionFillConfigWithDefaults(struct NoiseReductionConfig* config) {
|
||||
config->smoothing_bits = 10;
|
||||
config->even_smoothing = 0.025;
|
||||
config->odd_smoothing = 0.06;
|
||||
config->min_signal_remaining = 0.05;
|
||||
}
|
||||
|
||||
int NoiseReductionPopulateState(const struct NoiseReductionConfig* config,
|
||||
struct NoiseReductionState* state,
|
||||
int num_channels) {
|
||||
state->smoothing_bits = config->smoothing_bits;
|
||||
state->odd_smoothing = config->odd_smoothing * (1 << kNoiseReductionBits);
|
||||
state->even_smoothing = config->even_smoothing * (1 << kNoiseReductionBits);
|
||||
state->min_signal_remaining =
|
||||
config->min_signal_remaining * (1 << kNoiseReductionBits);
|
||||
state->num_channels = num_channels;
|
||||
state->estimate = calloc(state->num_channels, sizeof(*state->estimate));
|
||||
if (state->estimate == NULL) {
|
||||
fprintf(stderr, "Failed to alloc estimate buffer\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void NoiseReductionFreeStateContents(struct NoiseReductionState* state) {
|
||||
free(state->estimate);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* 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_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/noise_reduction.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct NoiseReductionConfig {
|
||||
// scale the signal up by 2^(smoothing_bits) before reduction
|
||||
int smoothing_bits;
|
||||
// smoothing coefficient for even-numbered channels
|
||||
float even_smoothing;
|
||||
// smoothing coefficient for odd-numbered channels
|
||||
float odd_smoothing;
|
||||
// fraction of signal to preserve (1.0 disables this module)
|
||||
float min_signal_remaining;
|
||||
};
|
||||
|
||||
// Populates the NoiseReductionConfig with "sane" default values.
|
||||
void NoiseReductionFillConfigWithDefaults(struct NoiseReductionConfig* config);
|
||||
|
||||
// Allocates any buffers.
|
||||
int NoiseReductionPopulateState(const struct NoiseReductionConfig* config,
|
||||
struct NoiseReductionState* state,
|
||||
int num_channels);
|
||||
|
||||
// Frees any allocated buffers.
|
||||
void NoiseReductionFreeStateContents(struct NoiseReductionState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_NOISE_REDUCTION_UTIL_H_
|
||||
@@ -0,0 +1,56 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h"
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/bits.h"
|
||||
|
||||
int16_t WideDynamicFunction(const uint32_t x, const int16_t* lut) {
|
||||
if (x <= 2) {
|
||||
return lut[x];
|
||||
}
|
||||
|
||||
const int16_t interval = MostSignificantBit32(x);
|
||||
lut += 4 * interval - 6;
|
||||
|
||||
const int16_t frac =
|
||||
((interval < 11) ? (x << (11 - interval)) : (x >> (interval - 11))) &
|
||||
0x3FF;
|
||||
|
||||
int32_t result = ((int32_t)lut[2] * frac) >> 5;
|
||||
result += (int32_t)((uint32_t)lut[1] << 5);
|
||||
result *= frac;
|
||||
result = (result + (1 << 14)) >> 15;
|
||||
result += lut[0];
|
||||
return (int16_t)result;
|
||||
}
|
||||
|
||||
uint32_t PcanShrink(const uint32_t x) {
|
||||
if (x < (2 << kPcanSnrBits)) {
|
||||
return (x * x) >> (2 + 2 * kPcanSnrBits - kPcanOutputBits);
|
||||
} else {
|
||||
return (x >> (kPcanSnrBits - kPcanOutputBits)) - (1 << kPcanOutputBits);
|
||||
}
|
||||
}
|
||||
|
||||
void PcanGainControlApply(struct PcanGainControlState* state,
|
||||
uint32_t* signal) {
|
||||
int i;
|
||||
for (i = 0; i < state->num_channels; ++i) {
|
||||
const uint32_t gain =
|
||||
WideDynamicFunction(state->noise_estimate[i], state->gain_lut);
|
||||
const uint32_t snr = ((uint64_t)signal[i] * gain) >> state->snr_shift;
|
||||
signal[i] = PcanShrink(snr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/* 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_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define kPcanSnrBits 12
|
||||
#define kPcanOutputBits 6
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Details at https://research.google/pubs/pub45911.pdf
|
||||
struct PcanGainControlState {
|
||||
int enable_pcan;
|
||||
uint32_t* noise_estimate;
|
||||
int num_channels;
|
||||
int16_t* gain_lut;
|
||||
int32_t snr_shift;
|
||||
};
|
||||
|
||||
int16_t WideDynamicFunction(const uint32_t x, const int16_t* lut);
|
||||
|
||||
uint32_t PcanShrink(const uint32_t x);
|
||||
|
||||
void PcanGainControlApply(struct PcanGainControlState* state, uint32_t* signal);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_H_
|
||||
@@ -0,0 +1,92 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control_util.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define kint16max 0x00007FFF
|
||||
|
||||
void PcanGainControlFillConfigWithDefaults(
|
||||
struct PcanGainControlConfig* config) {
|
||||
config->enable_pcan = 0;
|
||||
config->strength = 0.95;
|
||||
config->offset = 80.0;
|
||||
config->gain_bits = 21;
|
||||
}
|
||||
|
||||
int16_t PcanGainLookupFunction(const struct PcanGainControlConfig* config,
|
||||
int32_t input_bits, uint32_t x) {
|
||||
const float x_as_float = ((float)x) / ((uint32_t)1 << input_bits);
|
||||
const float gain_as_float =
|
||||
((uint32_t)1 << config->gain_bits) *
|
||||
powf(x_as_float + config->offset, -config->strength);
|
||||
|
||||
if (gain_as_float > kint16max) {
|
||||
return kint16max;
|
||||
}
|
||||
return (int16_t)(gain_as_float + 0.5f);
|
||||
}
|
||||
|
||||
int PcanGainControlPopulateState(const struct PcanGainControlConfig* config,
|
||||
struct PcanGainControlState* state,
|
||||
uint32_t* noise_estimate,
|
||||
const int num_channels,
|
||||
const uint16_t smoothing_bits,
|
||||
const int32_t input_correction_bits) {
|
||||
state->enable_pcan = config->enable_pcan;
|
||||
if (!state->enable_pcan) {
|
||||
return 1;
|
||||
}
|
||||
state->noise_estimate = noise_estimate;
|
||||
state->num_channels = num_channels;
|
||||
state->gain_lut = malloc(kWideDynamicFunctionLUTSize * sizeof(int16_t));
|
||||
if (state->gain_lut == NULL) {
|
||||
fprintf(stderr, "Failed to allocate gain LUT\n");
|
||||
return 0;
|
||||
}
|
||||
state->snr_shift = config->gain_bits - input_correction_bits - kPcanSnrBits;
|
||||
|
||||
const int32_t input_bits = smoothing_bits - input_correction_bits;
|
||||
state->gain_lut[0] = PcanGainLookupFunction(config, input_bits, 0);
|
||||
state->gain_lut[1] = PcanGainLookupFunction(config, input_bits, 1);
|
||||
state->gain_lut -= 6;
|
||||
int interval;
|
||||
for (interval = 2; interval <= kWideDynamicFunctionBits; ++interval) {
|
||||
const uint32_t x0 = (uint32_t)1 << (interval - 1);
|
||||
const uint32_t x1 = x0 + (x0 >> 1);
|
||||
const uint32_t x2 =
|
||||
(interval == kWideDynamicFunctionBits) ? x0 + (x0 - 1) : 2 * x0;
|
||||
|
||||
const int16_t y0 = PcanGainLookupFunction(config, input_bits, x0);
|
||||
const int16_t y1 = PcanGainLookupFunction(config, input_bits, x1);
|
||||
const int16_t y2 = PcanGainLookupFunction(config, input_bits, x2);
|
||||
|
||||
const int32_t diff1 = (int32_t)y1 - y0;
|
||||
const int32_t diff2 = (int32_t)y2 - y0;
|
||||
const int32_t a1 = 4 * diff1 - diff2;
|
||||
const int32_t a2 = diff2 - a1;
|
||||
|
||||
state->gain_lut[4 * interval] = y0;
|
||||
state->gain_lut[4 * interval + 1] = (int16_t)a1;
|
||||
state->gain_lut[4 * interval + 2] = (int16_t)a2;
|
||||
}
|
||||
state->gain_lut += 6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PcanGainControlFreeStateContents(struct PcanGainControlState* state) {
|
||||
free(state->gain_lut);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* 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_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/pcan_gain_control.h"
|
||||
|
||||
#define kWideDynamicFunctionBits 32
|
||||
#define kWideDynamicFunctionLUTSize (4 * kWideDynamicFunctionBits - 3)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct PcanGainControlConfig {
|
||||
// set to false (0) to disable this module
|
||||
int enable_pcan;
|
||||
// gain normalization exponent (0.0 disables, 1.0 full strength)
|
||||
float strength;
|
||||
// positive value added in the normalization denominator
|
||||
float offset;
|
||||
// number of fractional bits in the gain
|
||||
int gain_bits;
|
||||
};
|
||||
|
||||
void PcanGainControlFillConfigWithDefaults(
|
||||
struct PcanGainControlConfig* config);
|
||||
|
||||
int16_t PcanGainLookupFunction(const struct PcanGainControlConfig* config,
|
||||
int32_t input_bits, uint32_t x);
|
||||
|
||||
int PcanGainControlPopulateState(const struct PcanGainControlConfig* config,
|
||||
struct PcanGainControlState* state,
|
||||
uint32_t* noise_estimate,
|
||||
const int num_channels,
|
||||
const uint16_t smoothing_bits,
|
||||
const int32_t input_correction_bits);
|
||||
|
||||
void PcanGainControlFreeStateContents(struct PcanGainControlState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_PCAN_GAIN_CONTROL_UTIL_H_
|
||||
@@ -0,0 +1,70 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/window.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
int WindowProcessSamples(struct WindowState* state, const int16_t* samples,
|
||||
size_t num_samples, size_t* num_samples_read) {
|
||||
const int size = state->size;
|
||||
|
||||
// Copy samples from the samples buffer over to our local input.
|
||||
size_t max_samples_to_copy = state->size - state->input_used;
|
||||
if (max_samples_to_copy > num_samples) {
|
||||
max_samples_to_copy = num_samples;
|
||||
}
|
||||
memcpy(state->input + state->input_used, samples,
|
||||
max_samples_to_copy * sizeof(*samples));
|
||||
*num_samples_read = max_samples_to_copy;
|
||||
state->input_used += max_samples_to_copy;
|
||||
|
||||
if (state->input_used < state->size) {
|
||||
// We don't have enough samples to compute a window.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply the window to the input.
|
||||
const int16_t* coefficients = state->coefficients;
|
||||
const int16_t* input = state->input;
|
||||
int16_t* output = state->output;
|
||||
int i;
|
||||
int16_t max_abs_output_value = 0;
|
||||
for (i = 0; i < size; ++i) {
|
||||
int16_t new_value =
|
||||
(((int32_t)*input++) * *coefficients++) >> kFrontendWindowBits;
|
||||
*output++ = new_value;
|
||||
if (new_value < 0) {
|
||||
new_value = -new_value;
|
||||
}
|
||||
if (new_value > max_abs_output_value) {
|
||||
max_abs_output_value = new_value;
|
||||
}
|
||||
}
|
||||
// Shuffle the input down by the step size, and update how much we have used.
|
||||
memmove(state->input, state->input + state->step,
|
||||
sizeof(*state->input) * (state->size - state->step));
|
||||
state->input_used -= state->step;
|
||||
state->max_abs_output_value = max_abs_output_value;
|
||||
|
||||
// Indicate that the output buffer is valid for the next stage.
|
||||
return 1;
|
||||
}
|
||||
|
||||
void WindowReset(struct WindowState* state) {
|
||||
memset(state->input, 0, state->size * sizeof(*state->input));
|
||||
memset(state->output, 0, state->size * sizeof(*state->output));
|
||||
state->input_used = 0;
|
||||
state->max_abs_output_value = 0;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/* 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_MICROFRONTEND_LIB_WINDOW_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define kFrontendWindowBits 12
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct WindowState {
|
||||
size_t size;
|
||||
int16_t* coefficients;
|
||||
size_t step;
|
||||
|
||||
int16_t* input;
|
||||
size_t input_used;
|
||||
int16_t* output;
|
||||
int16_t max_abs_output_value;
|
||||
};
|
||||
|
||||
// Applies a window to the samples coming in, stepping forward at the given
|
||||
// rate.
|
||||
int WindowProcessSamples(struct WindowState* state, const int16_t* samples,
|
||||
size_t num_samples, size_t* num_samples_read);
|
||||
|
||||
void WindowReset(struct WindowState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_H_
|
||||
@@ -0,0 +1,73 @@
|
||||
/* 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 "tensorflow/lite/experimental/microfrontend/lib/window_util.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// Some platforms don't have M_PI
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
void WindowFillConfigWithDefaults(struct WindowConfig* config) {
|
||||
config->size_ms = 25;
|
||||
config->step_size_ms = 10;
|
||||
}
|
||||
|
||||
int WindowPopulateState(const struct WindowConfig* config,
|
||||
struct WindowState* state, int sample_rate) {
|
||||
state->size = config->size_ms * sample_rate / 1000;
|
||||
state->step = config->step_size_ms * sample_rate / 1000;
|
||||
|
||||
state->coefficients = malloc(state->size * sizeof(*state->coefficients));
|
||||
if (state->coefficients == NULL) {
|
||||
fprintf(stderr, "Failed to allocate window coefficients\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Populate the window values.
|
||||
const float arg = M_PI * 2.0 / ((float)state->size);
|
||||
int i;
|
||||
for (i = 0; i < state->size; ++i) {
|
||||
float float_value = 0.5 - (0.5 * cos(arg * (i + 0.5)));
|
||||
// Scale it to fixed point and round it.
|
||||
state->coefficients[i] =
|
||||
floor(float_value * (1 << kFrontendWindowBits) + 0.5);
|
||||
}
|
||||
|
||||
state->input_used = 0;
|
||||
state->input = malloc(state->size * sizeof(*state->input));
|
||||
if (state->input == NULL) {
|
||||
fprintf(stderr, "Failed to allocate window input\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->output = malloc(state->size * sizeof(*state->output));
|
||||
if (state->output == NULL) {
|
||||
fprintf(stderr, "Failed to allocate window output\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void WindowFreeStateContents(struct WindowState* state) {
|
||||
free(state->coefficients);
|
||||
free(state->input);
|
||||
free(state->output);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/* 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_MICROFRONTEND_LIB_WINDOW_UTIL_H_
|
||||
#define TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_UTIL_H_
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/window.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct WindowConfig {
|
||||
// length of window frame in milliseconds
|
||||
size_t size_ms;
|
||||
// length of step for next frame in milliseconds
|
||||
size_t step_size_ms;
|
||||
};
|
||||
|
||||
// Populates the WindowConfig with "sane" default values.
|
||||
void WindowFillConfigWithDefaults(struct WindowConfig* config);
|
||||
|
||||
// Allocates any buffers.
|
||||
int WindowPopulateState(const struct WindowConfig* config,
|
||||
struct WindowState* state, int sample_rate);
|
||||
|
||||
// Frees any allocated buffers.
|
||||
void WindowFreeStateContents(struct WindowState* state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXPERIMENTAL_MICROFRONTEND_LIB_WINDOW_UTIL_H_
|
||||
@@ -75,6 +75,7 @@ float ActivationFunction(float x) {
|
||||
inline void BiasAndClamp(float clamp_min, float clamp_max, int bias_size,
|
||||
const float* bias_data, int array_size,
|
||||
float* array_data) {
|
||||
if (bias_size == 0) return;
|
||||
// Note: see b/132215220: in May 2019 we thought it would be OK to replace
|
||||
// this with the Eigen one-liner:
|
||||
// return (array.colwise() + bias).cwiseMin(clamp_max).cwiseMin(clamp_max).
|
||||
@@ -138,6 +139,100 @@ inline void BiasAndClamp(float clamp_min, float clamp_max, int bias_size,
|
||||
#endif
|
||||
}
|
||||
|
||||
// Single-rounding MultiplyByQuantizedMultiplier
|
||||
#if TFLITE_SINGLE_ROUNDING
|
||||
inline int32_t MultiplyByQuantizedMultiplier(int32_t x,
|
||||
int32_t quantized_multiplier,
|
||||
int shift) {
|
||||
TFLITE_DCHECK(quantized_multiplier >= 0);
|
||||
TFLITE_DCHECK(shift >= -31 && shift <= 30);
|
||||
|
||||
const int64_t total_shift = 31 - shift;
|
||||
const int64_t round = static_cast<int64_t>(1) << (total_shift - 1);
|
||||
int64_t result = x * static_cast<int64_t>(quantized_multiplier) + round;
|
||||
result = result >> total_shift;
|
||||
|
||||
TFLITE_DCHECK(result >= std::numeric_limits<int32_t>::min() &&
|
||||
result <= std::numeric_limits<int32_t>::max());
|
||||
return static_cast<int32_t>(result);
|
||||
}
|
||||
|
||||
inline int32_t MultiplyByQuantizedMultiplierSmallerThanOneExp(
|
||||
int32_t x, int32_t quantized_multiplier, int shift) {
|
||||
TFLITE_DCHECK_LE(shift, 0);
|
||||
return MultiplyByQuantizedMultiplier(x, quantized_multiplier, shift);
|
||||
}
|
||||
|
||||
inline int32_t MultiplyByQuantizedMultiplierGreaterThanOne(
|
||||
int32_t x, int32_t quantized_multiplier, int shift) {
|
||||
TFLITE_DCHECK_GE(shift, 0);
|
||||
return MultiplyByQuantizedMultiplier(x, quantized_multiplier, shift);
|
||||
}
|
||||
|
||||
inline int32_t MultiplyByQuantizedMultiplier(int64_t x,
|
||||
int32_t quantized_multiplier,
|
||||
int shift) {
|
||||
// Inputs:
|
||||
// - quantized_multiplier has fixed point at bit 31
|
||||
// - shift is -31 to +7 (negative for right shift)
|
||||
//
|
||||
// Assumptions: The following input ranges are assumed
|
||||
// - quantize_scale>=0 (the usual range is (1<<30) to (1>>31)-1)
|
||||
// - scaling is chosen so final scaled result fits in int32_t
|
||||
// - input x is in the range -(1<<47) <= x < (1<<47)
|
||||
TFLITE_DCHECK(quantized_multiplier >= 0);
|
||||
TFLITE_DCHECK(shift >= -31 && shift < 8);
|
||||
TFLITE_DCHECK(x >= -(static_cast<int64_t>(1) << 47) &&
|
||||
x < (static_cast<int64_t>(1) << 47));
|
||||
|
||||
const int32_t reduced_multiplier =
|
||||
(quantized_multiplier < 0x7FFF0000)
|
||||
? ((quantized_multiplier + (1 << 15)) >> 16)
|
||||
: 0x7FFF;
|
||||
const int64_t total_shift = 15 - shift;
|
||||
const int64_t round = static_cast<int64_t>(1) << (total_shift - 1);
|
||||
int64_t result = x * static_cast<int64_t>(reduced_multiplier) + round;
|
||||
result = result >> total_shift;
|
||||
|
||||
TFLITE_DCHECK(result >= std::numeric_limits<int32_t>::min() &&
|
||||
result <= std::numeric_limits<int32_t>::max());
|
||||
return static_cast<int32_t>(result);
|
||||
}
|
||||
|
||||
#ifdef USE_NEON
|
||||
inline int32x4x4_t MultiplyByQuantizedMultiplier4Rows(
|
||||
int32x4x4_t input_val, int32_t quantized_multiplier, int shift) {
|
||||
TFLITE_DCHECK(quantized_multiplier >= 0);
|
||||
|
||||
const int right_shift = std::min(-1, shift);
|
||||
const int left_shift = shift - right_shift;
|
||||
|
||||
const int32x4_t multiplier_dup = vdupq_n_s32(quantized_multiplier);
|
||||
const int32x4_t left_shift_dup = vdupq_n_s32(left_shift);
|
||||
const int32x4_t right_shift_dup = vdupq_n_s32(right_shift);
|
||||
|
||||
int32x4x4_t result;
|
||||
result.val[0] = vrshlq_s32(
|
||||
vqdmulhq_s32(vshlq_s32(input_val.val[0], left_shift_dup), multiplier_dup),
|
||||
right_shift_dup);
|
||||
|
||||
result.val[1] = vrshlq_s32(
|
||||
vqdmulhq_s32(vshlq_s32(input_val.val[1], left_shift_dup), multiplier_dup),
|
||||
right_shift_dup);
|
||||
|
||||
result.val[2] = vrshlq_s32(
|
||||
vqdmulhq_s32(vshlq_s32(input_val.val[2], left_shift_dup), multiplier_dup),
|
||||
right_shift_dup);
|
||||
|
||||
result.val[3] = vrshlq_s32(
|
||||
vqdmulhq_s32(vshlq_s32(input_val.val[3], left_shift_dup), multiplier_dup),
|
||||
right_shift_dup);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif // USE_NEON
|
||||
// Double-rounding MultiplyByQuantizedMultiplier
|
||||
#else
|
||||
inline int32_t MultiplyByQuantizedMultiplierSmallerThanOneExp(
|
||||
int32_t x, int32_t quantized_multiplier, int left_shift) {
|
||||
using gemmlowp::RoundingDivideByPOT;
|
||||
@@ -224,7 +319,8 @@ inline int32x4x4_t MultiplyByQuantizedMultiplier4Rows(
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
#endif // USE_NEON
|
||||
#endif // TFLITE_SINGLE_ROUNDING
|
||||
|
||||
template <typename T>
|
||||
int CountLeadingZeros(T integer_input) {
|
||||
@@ -279,81 +375,125 @@ inline Integer FloorLog2(Integer n) {
|
||||
}
|
||||
}
|
||||
|
||||
// generate INT16 LUT for function(), e.g., table exp(x) and 1/(1+x) used in
|
||||
// softmax
|
||||
// func - the function to build the LUT for (e.g exp(x))
|
||||
// min,max - table limits
|
||||
// table - pointer to buffer
|
||||
// num - number of elements in the LUT
|
||||
inline void gen_lut(double (*func)(double), double min, double max,
|
||||
int16_t* table, const int num) {
|
||||
// size of table should equal to num + 1
|
||||
// last element only for slope calculation
|
||||
double step = (max - min) / (num - 1);
|
||||
double half_step = step / 2.0;
|
||||
for (int i = 0; i < num - 1; i++) {
|
||||
double sample_val = TfLiteRound(func(min + i * step) * 32768.0);
|
||||
double midpoint_interp_val =
|
||||
TfLiteRound((func(min + (i + 1) * step) * 32768.0 +
|
||||
TfLiteRound(func(min + i * step) * 32768.0)) /
|
||||
2.0);
|
||||
double midpoint_val =
|
||||
TfLiteRound(func(min + i * step + half_step) * 32768.0);
|
||||
double midpoint_err = midpoint_interp_val - midpoint_val;
|
||||
double bias = TfLiteRound(midpoint_err / 2.0);
|
||||
table[i] = std::min<double>(std::max<double>(sample_val - bias, -32768.0),
|
||||
32767.0);
|
||||
}
|
||||
table[num - 1] = std::min<double>(
|
||||
std::max<double>(TfLiteRound(func(max) * 32768.0), -32768.0), 32767.0);
|
||||
// The size of the LUT depends on the type of input. For int8 inputs a simple
|
||||
// 256 entries LUT is used. For int16 inputs the high 9 bits are used for
|
||||
// indexing and the 7 remaining bits are used for interpolation. We thus use a
|
||||
// 513-entries LUT for int16 cases, 512 for the 9-bit indexing and 1 extra entry
|
||||
// to interpolate the last value.
|
||||
template <typename LutInT>
|
||||
constexpr int lut_size() {
|
||||
static_assert(std::is_same<LutInT, int8_t>::value ||
|
||||
std::is_same<LutInT, int16_t>::value,
|
||||
"Only LUTs with int8 or int16 inputs are supported.");
|
||||
return std::is_same<LutInT, int8_t>::value ? 256 : 513;
|
||||
}
|
||||
|
||||
// generate INT16 LUT for function(), e.g., table exp(x) and 1/(1+x) used in
|
||||
// softmax
|
||||
// func - the function to build the LUT for (e.g exp(x))
|
||||
// min,max - table limits
|
||||
// table - pointer to buffer
|
||||
// num - number of elements in the LUT
|
||||
inline void gen_lut(float (*func)(float), float min, float max, int16_t* table,
|
||||
const int num) {
|
||||
// size of table should equal to num + 1
|
||||
// last element only for slope calculation
|
||||
float step = (max - min) / (num - 1);
|
||||
float half_step = step / 2.0f;
|
||||
for (int i = 0; i < num - 1; i++) {
|
||||
float sample_val = TfLiteRound(func(min + i * step) * 32768.0f);
|
||||
float midpoint_interp_val =
|
||||
TfLiteRound((func(min + (i + 1) * step) * 32768.0f +
|
||||
TfLiteRound(func(min + i * step) * 32768.0f)) /
|
||||
2.0f);
|
||||
float midpoint_val =
|
||||
TfLiteRound(func(min + i * step + half_step) * 32768.0f);
|
||||
float midpoint_err = midpoint_interp_val - midpoint_val;
|
||||
float bias = TfLiteRound(midpoint_err / 2.0f);
|
||||
table[i] = std::min<float>(std::max<float>(sample_val - bias, -32768.0f),
|
||||
32767.0f);
|
||||
// Generate a LUT for 'func' which can be used to approximate functions like
|
||||
// exp, log, ...
|
||||
//
|
||||
// - func: the function to build the LUT for (e.g exp(x))
|
||||
// - input_min, input_max: range of the func inputs
|
||||
// - output_min, output_max: range of the func outputs
|
||||
// - lut: pointer to the LUT table to fill, the table must be of size
|
||||
// lut_size<LutInT>()
|
||||
template <typename FloatT, typename LutInT, typename LutOutT>
|
||||
inline void gen_lut(FloatT (*func)(FloatT), FloatT input_min, FloatT input_max,
|
||||
FloatT output_min, FloatT output_max, LutOutT* lut) {
|
||||
static_assert(std::is_same<LutInT, int8_t>::value ||
|
||||
std::is_same<LutInT, int16_t>::value,
|
||||
"Only LUTs with int8 or int16 inputs are supported.");
|
||||
static_assert(std::is_same<LutOutT, int8_t>::value ||
|
||||
std::is_same<LutOutT, int16_t>::value,
|
||||
"Only LUTs with int8 or int16 outputs are supported.");
|
||||
static_assert(std::is_floating_point<FloatT>::value,
|
||||
"FloatT must be a floating-point type.");
|
||||
|
||||
const int nb_steps = std::is_same<LutInT, int8_t>::value ? 256 : 512;
|
||||
const FloatT step = (input_max - input_min) / nb_steps;
|
||||
const FloatT half_step = step / 2;
|
||||
const FloatT output_scaling_inv =
|
||||
static_cast<FloatT>(std::numeric_limits<LutOutT>::max() -
|
||||
std::numeric_limits<LutOutT>::min() + 1) /
|
||||
(output_max - output_min);
|
||||
const FloatT table_min =
|
||||
static_cast<FloatT>(std::numeric_limits<LutOutT>::min());
|
||||
const FloatT table_max =
|
||||
static_cast<FloatT>(std::numeric_limits<LutOutT>::max());
|
||||
|
||||
for (int i = 0; i < nb_steps; i++) {
|
||||
const FloatT val = func(input_min + i * step);
|
||||
const FloatT val_midpoint = func(input_min + i * step + half_step);
|
||||
const FloatT val_next = func(input_min + (i + 1) * step);
|
||||
|
||||
const FloatT sample_val = TfLiteRound(val * output_scaling_inv);
|
||||
const FloatT midpoint_interp_val =
|
||||
TfLiteRound((val_next * output_scaling_inv +
|
||||
TfLiteRound(val * output_scaling_inv)) /
|
||||
2);
|
||||
const FloatT midpoint_val = TfLiteRound(val_midpoint * output_scaling_inv);
|
||||
const FloatT midpoint_err = midpoint_interp_val - midpoint_val;
|
||||
const FloatT bias = TfLiteRound(midpoint_err / 2);
|
||||
|
||||
lut[i] = static_cast<LutOutT>(std::min<FloatT>(
|
||||
std::max<FloatT>(sample_val - bias, table_min), table_max));
|
||||
}
|
||||
|
||||
const bool with_extra_interpolation_value =
|
||||
std::is_same<LutInT, int16_t>::value;
|
||||
if (with_extra_interpolation_value) {
|
||||
lut[nb_steps] = static_cast<LutOutT>(std::min<FloatT>(
|
||||
std::max<FloatT>(TfLiteRound(func(input_max) * output_scaling_inv),
|
||||
table_min),
|
||||
table_max));
|
||||
}
|
||||
table[num - 1] = std::min<float>(
|
||||
std::max<float>(TfLiteRound(func(max) * 32768.0f), -32768.0f), 32767.0f);
|
||||
}
|
||||
|
||||
// int16_t func table lookup, e.g., lookup exp() and 1/(1+x) used in softmax
|
||||
inline int16_t generic_int16_table_lookup(int16_t value, const int16_t* lut) {
|
||||
// 512 base value, lut[513] only for calculate slope
|
||||
uint16_t index = static_cast<uint16_t>(256 + (value >> 7));
|
||||
// LUT must have 513 values
|
||||
template <typename LutOutT>
|
||||
inline LutOutT lut_lookup_with_interpolation(int16_t value,
|
||||
const LutOutT* lut) {
|
||||
static_assert(std::is_same<LutOutT, int8_t>::value ||
|
||||
std::is_same<LutOutT, int16_t>::value,
|
||||
"Only LUTs with int8 or int16 outputs are supported.");
|
||||
// 512 base values, lut[513] is only used to calculate the slope
|
||||
const uint16_t index = static_cast<uint16_t>(256 + (value >> 7));
|
||||
assert(index < 512 && "LUT index out of range.");
|
||||
int16_t offset = value & 0x7f;
|
||||
const int16_t offset = value & 0x7f;
|
||||
|
||||
// base and slope are Q0.15
|
||||
int16_t base = lut[index];
|
||||
int16_t slope = lut[index + 1] - lut[index];
|
||||
// Base and slope are Q0.x
|
||||
const LutOutT base = lut[index];
|
||||
const LutOutT slope = lut[index + 1] - lut[index];
|
||||
|
||||
// Q0.15 * Q0.7 = Q0.22
|
||||
// Round and convert from Q0.22 to Q0.15
|
||||
int32_t delta = (static_cast<int32_t>(slope) * offset + 64) >> 7;
|
||||
// Q0.x * Q0.7 = Q0.(x + 7)
|
||||
// Round and convert from Q0.(x + 7) to Q0.x
|
||||
const int delta = (slope * offset + 64) >> 7;
|
||||
|
||||
// Q0.15 + Q0.15
|
||||
return base + delta;
|
||||
return static_cast<LutOutT>(base + delta);
|
||||
}
|
||||
|
||||
// int16_t -> int16_t table lookup with interpolation
|
||||
// LUT must have 513 values
|
||||
inline int16_t lut_lookup(int16_t value, const int16_t* lut) {
|
||||
return lut_lookup_with_interpolation(value, lut);
|
||||
}
|
||||
|
||||
// int16_t -> int8_t table lookup with interpolation
|
||||
// LUT must have 513 values
|
||||
inline int8_t lut_lookup(int16_t value, const int8_t* lut) {
|
||||
return lut_lookup_with_interpolation(value, lut);
|
||||
}
|
||||
|
||||
// int8_t -> int8_t table lookup without interpolation
|
||||
// LUT must have 256 values
|
||||
inline int8_t lut_lookup(int8_t value, const int8_t* lut) {
|
||||
return lut[128 + value];
|
||||
}
|
||||
|
||||
// int8_t -> int16_t table lookup without interpolation
|
||||
// LUT must have 256 values
|
||||
inline int16_t lut_lookup(int8_t value, const int16_t* lut) {
|
||||
return lut[128 + value];
|
||||
}
|
||||
|
||||
// Table of sigmoid(i/24) at 0.16 format - 256 elements.
|
||||
@@ -575,7 +715,8 @@ log_x_for_x_greater_than_or_equal_to_1_impl(
|
||||
// InputIntegerBits - z_b_headroom - 0.25);
|
||||
const FixedPointAccum z_a_pow_2_adj = SaturatingAddNonGemmlowp(
|
||||
FixedPointAccum::FromRaw(SaturatingRoundingMultiplyByPOTParam(
|
||||
InputIntegerBits - z_a_headroom_plus_1, 31 - kAccumIntegerBits)),
|
||||
static_cast<int32_t>(InputIntegerBits - z_a_headroom_plus_1),
|
||||
31 - kAccumIntegerBits)),
|
||||
shifted_quarter);
|
||||
|
||||
// z_b is treated like z_a, but premultiplying by sqrt(0.5).
|
||||
@@ -585,7 +726,8 @@ log_x_for_x_greater_than_or_equal_to_1_impl(
|
||||
SaturatingRoundingMultiplyByPOTParam(z_a.raw(), z_b_headroom);
|
||||
const FixedPointAccum z_b_pow_2_adj = SaturatingSub(
|
||||
FixedPointAccum::FromRaw(SaturatingRoundingMultiplyByPOTParam(
|
||||
InputIntegerBits - z_b_headroom, 31 - kAccumIntegerBits)),
|
||||
static_cast<int32_t>(InputIntegerBits - z_b_headroom),
|
||||
31 - kAccumIntegerBits)),
|
||||
shifted_quarter);
|
||||
|
||||
const FixedPoint0 r = FixedPoint0::FromRaw(std::min(r_a_raw, r_b_raw));
|
||||
@@ -19,9 +19,8 @@ limitations under the License.
|
||||
|
||||
namespace tflite {
|
||||
|
||||
#if defined(TF_LITE_USE_GLOBAL_CMATH_FUNCTIONS) || \
|
||||
(defined(__ANDROID__) && !defined(__NDK_MAJOR__)) || defined(ARDUINO) || \
|
||||
defined(__ZEPHYR__)
|
||||
#if defined(TF_LITE_USE_GLOBAL_CMATH_FUNCTIONS) || \
|
||||
(defined(__ANDROID__) && !defined(__NDK_MAJOR__)) || defined(__ZEPHYR__)
|
||||
#define TF_LITE_GLOBAL_STD_PREFIX
|
||||
#else
|
||||
#define TF_LITE_GLOBAL_STD_PREFIX std
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user