Compare commits

..

91 Commits

Author SHA1 Message Date
jomjol
2daf6c8b3f v10.1.0 2022-01-09 09:47:16 +01:00
jomjol
24b46158de v10.1.0 2022-01-09 09:43:22 +01:00
jomjol
63d336ba28 Rolling 20220104 2022-01-04 21:17:20 +01:00
jomjol
8dd3a92671 Rolling 20220103 2022-01-03 17:32:01 +01:00
jomjol
8e8897c70d v10.0.2 2022-01-01 08:57:07 +01:00
jomjol
eac513411e v10.0.2 2022-01-01 08:53:55 +01:00
jomjol
98f9274085 V10.0.1 2021-12-31 09:02:59 +01:00
jomjol
884dd9fc3b V10.0.0 2021-12-30 18:38:15 +01:00
jomjol
04c564936a Master 10.0.0 2021-12-30 18:29:01 +01:00
jomjol
957138960a Rolling 20211230 2021-12-30 11:02:20 +01:00
jomjol
58a0297915 Rolling 20211223 2021-12-23 08:03:46 +01:00
jomjol
61bf536207 Rolling 20211212 2021-12-12 19:30:07 +01:00
jomjol
4136a7b372 Merge pull request #456 from mad2xlc/master
tabindex to html
2021-12-12 17:45:25 +01:00
Stefan
b3afc9961d Merge branch 'jomjol:master' into master 2021-12-12 10:36:53 +01:00
jomjol
27e2e042da Rolling 2021-21-03 2021-12-03 18:12:45 +01:00
Frederik Kemner
cc659bad30 Time based flow rate limiting
Take time since last valid value into account for flow rate limiting
2021-12-03 11:27:20 +01:00
jomjol
776931c0ad v9.2.0 2021-12-02 21:56:05 +01:00
jomjol
e22b4b6fe1 v9.2.0 2021-12-02 21:52:45 +01:00
jomjol
cad534bffe update wiki 2021-12-02 21:47:37 +01:00
jomjol
3b44adb6fa Rolling 20211128 2021-11-28 09:09:24 +01:00
jomjol
cc0bd1473f Rolling 20211124 2021-11-24 07:32:03 +01:00
jomjol
58124d27bf Rolling 2021-11-23 2021-11-23 17:57:07 +01:00
Sven Rojek
9ad118814a add basic exception handling for the camera module 2021-11-21 19:05:17 +01:00
jomjol
270f8dd093 v9.1.1 2021-11-16 07:15:55 +01:00
jomjol
fde0ae4781 v9.1.0 2021-11-14 08:57:19 +01:00
jomjol
b87ce8c75d Merge branch 'master' into rolling 2021-11-14 08:52:40 +01:00
jomjol
e372c87836 v9.1.0 2021-11-14 08:52:28 +01:00
jomjol
f8478d7742 Merge branch 'master' into rolling 2021-11-14 08:48:42 +01:00
jomjol
a3d6923fec v9.1.0 2021-11-14 08:47:55 +01:00
jomjol
7bfa22187d Merge branch 'pr/398' into rolling 2021-11-14 08:19:13 +01:00
pixel::doc
7a8affae32 Update Helper.cpp
Change "open config file" to "open file"
2021-11-14 01:05:40 +01:00
jomjol
e48dd7c4c4 rolling 20211106 2021-11-06 22:46:16 +01:00
mad2xlc
6d298c1746 added tabindex for coordinate fields 2021-11-04 17:17:22 +01:00
jomjol
4fe9ab92e0 image update 2021-10-27 18:57:07 +02:00
jomjol
f2ac4cdc64 v9.0.0 2021-10-23 16:35:40 +02:00
jomjol
be902401d1 Merge branch 'rolling' 2021-10-23 16:32:44 +02:00
jomjol
020e93bca2 v9.0.0 2021-10-23 16:31:55 +02:00
jomjol
61dfdc2258 Add files via upload 2021-10-23 15:44:46 +02:00
jomjol
a98e3c8eab Add files via upload 2021-10-22 21:31:21 +02:00
jomjol
58a90ff706 v8.5.0 2021-10-07 07:45:40 +02:00
jomjol
d0bf12f3d4 v8.5.0 2021-10-07 07:16:46 +02:00
jomjol
af16785bbf Rolling 20211002 2021-10-02 14:59:09 +02:00
jomjol
18f6e83a2c v8.4.0 2021-09-25 18:57:40 +02:00
jomjol
147d97421b Merge branch 'rolling' 2021-09-25 18:53:47 +02:00
jomjol
dcf2feb7aa v8.4.0 2021-09-25 18:53:14 +02:00
jomjol
e63e940b96 v8.4.0 2021-09-25 08:08:21 +02:00
jomjol
68b0fb83ee v8.4.0 2021-09-24 19:57:48 +02:00
jomjol
f15e5f060a v8.4.0 2021-09-23 18:43:53 +02:00
jomjol
e2a403441f Rolling 20210922 2021-09-22 22:13:08 +02:00
jomjol
9b3665b9c6 Rolling 20210921 v2 2021-09-21 19:41:20 +02:00
jomjol
f4c8bf9206 Rolling 20210921 2021-09-21 18:49:32 +02:00
jomjol
c033db9c31 Rolling 20210921 2021-09-21 07:27:46 +02:00
jomjol
9300526f49 Rolling 2021-09-20 2021-09-20 21:18:34 +02:00
jomjol
b6dd1f7f2d Update 2021-09-14 20:00:45 +02:00
jomjol
1e6eddca04 Rolling 20210913 2021-09-13 20:05:54 +02:00
jomjol
19ca0d7dd7 Update Versioninfo 2021-09-12 07:33:30 +02:00
jomjol
7fcb5d1c0c v8.3.0 2021-09-12 07:29:30 +02:00
jomjol
dd995ec28a Rolling 20210910 2021-09-10 09:26:52 +02:00
jomjol
af99de3535 IgnoreLeadingNaN 2021-09-02 11:04:01 +02:00
jomjol
3567cc2fb0 Merge pull request #330 from pixeldoc2000/pixeldoc2000-patch-1
Pixeldoc2000 patch 1
2021-09-02 11:00:49 +02:00
pixel::doc
5e9d9bd264 Update edit_config_param.html
Fixed some more Text.
2021-09-02 10:09:36 +02:00
pixel::doc
62447c1bb9 Update edit_config_param.html
Fixed some Text
2021-09-02 00:23:59 +02:00
jomjol
a86434c9a2 Rolling 20210831 2021-08-31 11:40:29 +02:00
jomjol
b7b70299f7 Rolling 20210830 2021-08-30 21:21:18 +02:00
jomjol
eb02e0aec1 new images 2021-08-29 20:57:21 +02:00
jomjol
7816e53db7 v8.2.0 2021-08-24 08:37:18 +02:00
jomjol
7ae08e572a Merge branch 'rolling' 2021-08-24 08:34:32 +02:00
jomjol
47d15d8adb v8.2.0 2021-08-24 08:33:56 +02:00
jomjol
0dac0e87e4 rolling 20210823 2021-08-23 18:57:48 +02:00
jomjol
b290099d5b v12.0.0 2021-08-12 07:25:12 +02:00
jomjol
f6b1a41a0b v12.0.0 2021-08-12 07:20:58 +02:00
jomjol
e529af04cf Update FeatureRequest.md 2021-08-10 20:19:05 +02:00
jomjol
6c365dd949 rolling v20210809 2021-08-09 21:53:07 +02:00
jomjol
32f15fc557 rolling 20210708 2021-08-07 15:25:27 +02:00
jomjol
6f06af1d5f Update README.md 2021-08-01 21:52:00 +02:00
jomjol
a91f99faab update 2021-08-01 21:49:29 +02:00
jomjol
17a87b23a1 v8.0.5 2021-08-01 21:46:17 +02:00
jomjol
d4b5ec2ae2 v8.0.4 2021-07-29 20:18:53 +02:00
jomjol
1bcaf09855 v8.0.4 2021-07-29 20:14:36 +02:00
jomjol
fa3842b2b4 v8.0.3 2021-07-25 18:15:35 +02:00
jomjol
ea72256e56 Merge branch 'rolling' 2021-07-25 18:08:43 +02:00
jomjol
be5828cb3e v8.0.3 2021-07-25 18:07:50 +02:00
jomjol
104b72505c Rolling - 20210725 2021-07-25 13:44:22 +02:00
jomjol
23728a0686 Update README.md 2021-07-23 21:02:19 +02:00
jomjol
eaaa856b13 Update README.md 2021-07-23 21:01:50 +02:00
jomjol
01e81d02b5 v8.0.2 2021-07-23 20:57:42 +02:00
jomjol
9ae8d0a512 Merge branch 'rolling' 2021-07-23 20:48:28 +02:00
jomjol
da16322fb8 v8.0.2 2021-07-23 20:47:23 +02:00
jomjol
a6d39afc26 rolling 20210723 2021-07-23 07:44:59 +02:00
jomjol
1b6a124f54 Update FeatureRequest.md 2021-07-21 07:00:12 +02:00
jomjol
8aff6bf8f3 Rolling 20210719 2021-07-19 21:54:14 +02:00
452 changed files with 27489 additions and 10427 deletions

View File

@@ -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)

View File

@@ -11,6 +11,77 @@
____
#### #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
* Implementation of an authentication mechanism.
#### #8 MQTT configurable readout intervall
Make the readout intervall configurable via MQTT.
@@ -27,13 +98,13 @@ To do:
* Implement a checking algo
* Extend the firmware and html page for the error handling
#### #6 Check for double ROI names
#### ~~#6 Check for double ROI names~~ - implemented v8.0.0
Check during configuration, that ROI names are unique.
~~Check during configuration, that ROI names are unique.~~
To do:
~~To do:~~
* Implementation of ROI name checking in html code before saving analog or digital ROIs
* ~~Implementation of ROI name checking in html code before saving analog or digital ROIs~~
@@ -49,31 +120,31 @@ To do:
#### #4 Initial Shifting and Rotation
#### ~~#4 Initial Shifting and Rotation~~ - implemented v7.0.0
* https://github.com/jomjol/AI-on-the-edge-device/issues/123
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/123~~
Implementation of a shifting additional to the initial rotation of the raw camera input
~~Implementation of a shifting additional to the initial rotation of the raw camera input~~
To do:
~~To do:~~
* Implementation of shifting
* Extension of configuration
* Adaption of the html configuration to implement shifting
* ~~Implementation of shifting~~
* ~~Extension of configuration~~
* ~~Adaption of the html configuration to implement shifting~~
#### #3 Allow grouping of digits to multiple reading values
#### ~~#3 Allow grouping of digits to multiple reading values~~ - implemented v8.0.0
* https://github.com/jomjol/AI-on-the-edge-device/issues/123
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/123~~
Implementation of two different independent readouts in one setup
~~Implementation of two different independent readouts in one setup~~
To do:
~~To do:~~
* ~~Extend the configuration, setting and processing flow for two independend readouts~~
* Extend the configuration, setting and processing flow for two independend readouts
https://github.com/jomjol/AI-on-the-edge-device/issues/123
@@ -96,15 +167,16 @@ To do:
____
#### #1 Optional GPIO for external flash/lighting
#### ~~#1 Optional GPIO for external flash/lighting~~ - implemented (v8.0.0)
* https://github.com/jomjol/AI-on-the-edge-device/issues/133
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/133~~
Implementation of an an extrnal flash / lightning through GPIOs.
* available GPIOs: 12 & 13 (currently in use for html switching)
~~Implementation of an an extrnal flash / lightning through GPIOs.~~
To do:
* ~~available GPIOs: 12 & 13 (currently in use for html switching)~~
* Implementation of a software module for external light source (e.g. WS8132 LED controller, ...)
* Update of the camera module to use the external light instead of the internal flash light
* Adopt the configuration algorithm with a configurable light source
~~To do:~~
* ~~Implementation of a software module for external light source (e.g. WS8132 LED controller, ...)~~
* ~~Update of the camera module to use the external light instead of the internal flash light~~
* ~~Adopt the configuration algorithm with a configurable light source~~

21
LICENSE
View File

@@ -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.

View File

@@ -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,57 +34,84 @@ 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.1 - Multi Meter Support (2021-07-18)
* 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.0 - Stability Increase (2022-01-09)
* 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
- 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
@@ -95,6 +124,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)
@@ -117,8 +150,3 @@ There are some ideas and feature request, which are not followed currently - mai
#### [Full Changelog](Changelog.md)
## Solved topics
* n.a.

View 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"

View File

@@ -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;

View 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;
}

View 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; }
};

View 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;
}
}

View 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 ];
};

View File

@@ -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
}
}
}
}
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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__ */

View File

@@ -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";

View File

@@ -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)

View File

@@ -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);
*/
}

View File

@@ -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"
@@ -107,8 +114,12 @@ esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename)
return httpd_resp_set_type(req, "text/html");
} else if (IS_FILE_EXT(filename, ".jpeg")) {
return httpd_resp_set_type(req, "image/jpeg");
} else if (IS_FILE_EXT(filename, ".jpg")) {
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 */

View File

@@ -30,6 +30,7 @@
#include "server_file.h"
#include "server_GPIO.h"
#include "ClassLogFile.h"
#include "Helper.h"

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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";};
};

View 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);
}
}

View 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

View File

@@ -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;
}

View File

@@ -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

View 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

View File

@@ -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);
}
}

View File

@@ -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";};
};

View File

@@ -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 {

View File

@@ -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";

View File

@@ -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";};

View File

@@ -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" + to_string(NUMBERS[j]->PreValue) + "\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: " + std::to_string(NUMBERS[j]->Value) + " ";
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);

View File

@@ -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

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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)

View File

@@ -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";

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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);
}
*/
}

View File

@@ -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;

View File

@@ -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")){

View 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)

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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, &reg);
// }
// 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, &reg);
// 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"

View File

@@ -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.

View File

@@ -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);

View File

@@ -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 "

View File

@@ -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

View File

@@ -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_

View File

@@ -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));
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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));
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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};

View File

@@ -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_

View File

@@ -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;
}

View File

@@ -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_

View File

@@ -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;
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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);
}
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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;
}

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -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));

View File

@@ -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