mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-09 21:17:06 +03:00
Compare commits
125 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bce99da6e5 | ||
|
|
234925c850 | ||
|
|
c9b7a5f84c | ||
|
|
338184712d | ||
|
|
b2c510d73e | ||
|
|
6a5d5511f1 | ||
|
|
ec99ac3a3b | ||
|
|
f676f12e02 | ||
|
|
296a50a6d2 | ||
|
|
b1ee3d8793 | ||
|
|
993fbfe5a8 | ||
|
|
2b60e81a52 | ||
|
|
11418459b8 | ||
|
|
3b8b8e47da | ||
|
|
ae302d49ef | ||
|
|
0153229d3c | ||
|
|
eeb74dd6fd | ||
|
|
ecc62a3ba9 | ||
|
|
aca60465f0 | ||
|
|
57bdca37fc | ||
|
|
6409397770 | ||
|
|
974044adf0 | ||
|
|
59aeeda786 | ||
|
|
b6bf8d992f | ||
|
|
9d31edc67a | ||
|
|
1b4f4bdd6d | ||
|
|
c9a879d329 | ||
|
|
ea69b1be00 | ||
|
|
2a8b3a87ea | ||
|
|
52783734ce | ||
|
|
0e1b390ec6 | ||
|
|
ab49bdf82f | ||
|
|
25e7051271 | ||
|
|
7315f9adfc | ||
|
|
af1aee4ac3 | ||
|
|
d6ff7eef88 | ||
|
|
7706b4dbc3 | ||
|
|
3561ecd2b7 | ||
|
|
74c7ff7fdf | ||
|
|
a68ce353ad | ||
|
|
0d168f3445 | ||
|
|
073e04a3cc | ||
|
|
591dc048d4 | ||
|
|
bfe8d3b37a | ||
|
|
9695dba415 | ||
|
|
6a48f0502e | ||
|
|
4a8d6592ab | ||
|
|
434aebd641 | ||
|
|
c124c38e70 | ||
|
|
e6d60bb124 | ||
|
|
085ea2028c | ||
|
|
0e7c600cf7 | ||
|
|
3f3532defe | ||
|
|
a0ffc88e47 | ||
|
|
11bfaf0e91 | ||
|
|
189093d548 | ||
|
|
34eb89b1b6 | ||
|
|
b725d242d3 | ||
|
|
568e88314b | ||
|
|
aaad952782 | ||
|
|
dc27911174 | ||
|
|
42678ae8e1 | ||
|
|
b6341992f6 | ||
|
|
17fd0f96df | ||
|
|
0b039e8d8c | ||
|
|
eab8a6d96b | ||
|
|
9f72bf117e | ||
|
|
058e9436f0 | ||
|
|
ebd8fe0e4f | ||
|
|
7970f5f691 | ||
|
|
0689ca86c5 | ||
|
|
5783e0f182 | ||
|
|
e190aa8d03 | ||
|
|
15bac02cbf | ||
|
|
7fdacce9a2 | ||
|
|
78ea179e9a | ||
|
|
2cfc3fadb2 | ||
|
|
60cdee2ea6 | ||
|
|
a17ac6860d | ||
|
|
ef24466702 | ||
|
|
de78027b0e | ||
|
|
28c40a0ad2 | ||
|
|
230bbb2239 | ||
|
|
bda2913a32 | ||
|
|
78daaf6e5b | ||
|
|
8939167a39 | ||
|
|
b48c6111d9 | ||
|
|
de1e919b96 | ||
|
|
35a96027f1 | ||
|
|
7a36bfa2ff | ||
|
|
dfeac0cb7f | ||
|
|
dfec780157 | ||
|
|
4cc93324f8 | ||
|
|
bf8833eae6 | ||
|
|
00028010ee | ||
|
|
cce812ff11 | ||
|
|
ccb4bd595c | ||
|
|
dc7eedcd8f | ||
|
|
40faa78929 | ||
|
|
eb53db00d0 | ||
|
|
658ef39736 | ||
|
|
0e90bcb2ef | ||
|
|
a020fce2d7 | ||
|
|
525ccf7aba | ||
|
|
ebcfc16f63 | ||
|
|
4b825efffb | ||
|
|
71871016d2 | ||
|
|
c07ef23d5b | ||
|
|
cbe884ad63 | ||
|
|
1dd703b337 | ||
|
|
bcb1d98191 | ||
|
|
1f5486e8cc | ||
|
|
1371be6f2c | ||
|
|
b0d8ed6248 | ||
|
|
379f4585e3 | ||
|
|
45a71981c8 | ||
|
|
ac3409f644 | ||
|
|
11c33f81dd | ||
|
|
641cc860d8 | ||
|
|
2029bd6e8a | ||
|
|
1ca5e1218d | ||
|
|
887c704f63 | ||
|
|
567dc74cd7 | ||
|
|
53606d5055 | ||
|
|
19a6c21c44 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
.code-workspace
|
||||
/sd-card/htm./.vscode/
|
||||
/code/build
|
||||
/sd-card/html/debug/
|
||||
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
@@ -15,3 +16,5 @@ install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
code/edgeAI.code-workspace
|
||||
.DS_Store
|
||||
|
||||
146
Changelog.md
146
Changelog.md
@@ -1,5 +1,151 @@
|
||||
# Versions
|
||||
|
||||
##### 10.6.2 - Stability Increase (2022-07-24)
|
||||
|
||||
- **NEW 10.6.2**: ignore hidden files in model selection (configuration page)
|
||||
|
||||
- **NEW 10.6.1**: Revoke esp32cam & tflite update
|
||||
|
||||
- **NEW 10.6.1**: Bug Fix: tflite-filename with ".", HTML spelling error
|
||||
|
||||
- IndluxDB: direct injection into InfluxDB - thanks to **[wetneb](https://github.com/wetneb)**
|
||||
|
||||
- MQTT: implemented "Retain Flag" and extend with absolute Change (in addition to rate)
|
||||
|
||||
- `config.ini`: removal of modelsize (readout from tflite)
|
||||
|
||||
- Updated analog neural network file (`ana1000s2.tflite`) & digital neural network file (`dig1400s2q.tflite`)
|
||||
|
||||
- TFMicro/Lite: Update (espressif Version 20220716)
|
||||
|
||||
- Updated esp32cam (v20220716)
|
||||
|
||||
- ESP-IDF: Update to 4.4
|
||||
|
||||
- Internal update (CNN algorithm optimizations, reparation for new neural network type)
|
||||
|
||||
- Bug Fix: no time with fixed IP, Postprocessing, MQTT
|
||||
|
||||
|
||||
|
||||
##### 10.5.2 - Stability Increase (2022-02-22)
|
||||
|
||||
- NEW 10.5.2: Bug Fix: wrong `firmware.bin` (no rate update)
|
||||
- NEW 10.5.1: Bug Fix: wrong return value, rate value & PreValue status, HTML: SSID & IP were not displayed
|
||||
- MQTT: changed wifi naming to "wifiRSSI"
|
||||
- HTML: check selectable values for consistency
|
||||
- Refactoring of check postprocessing consistency (e.g. max rate, negative rate, ...)
|
||||
- Bug Fix: corrected error in "Check Consistency Increase"
|
||||
|
||||
|
||||
|
||||
##### 10.4.0 - Stability Increase (2022-02-12)
|
||||
|
||||
- Graphical configuration: select available neural network files (*.tfl, *.tflite) from drop down menu
|
||||
- OTA-update: add option to upload tfl / tflite files to the correct location (`/config/`)
|
||||
- In the future the new files will also be copied to the `firmware` directory of the repository
|
||||
- Added Wifi RSSI to MQTT information
|
||||
- Updated analog neural network file (`ana-s3-q-20220105.tflite`)
|
||||
- Updated digital neural network file (`dig-s1-q-20220102.tflite`)
|
||||
- Updated build environment to `Espressif 3.5.0`
|
||||
|
||||
|
||||
|
||||
##### 10.3.0 - Stability Increase (2022-01-29)
|
||||
|
||||
- Implemented LED flash dimming (`LEDIntensity`).
|
||||
Remark: as auto illumination in the camera is used, this is rather for energy saving. It will not help reducing reflections
|
||||
- Additional camera parameters: saturation, contrast (although not too much impact yet)
|
||||
- Some readings will have removable "N"s that can not be removed automatically and are handled with an "error" --> no return value in the field "value" anymore (still reported back via field "raw value")
|
||||
- Updated esp32 camera hardware driver
|
||||
- Bug fix: MQTT, HTML improvements
|
||||
|
||||
**ATTENTION: The new ESP32 camera hardware driver is much more stable on newer OV2640 versions (no or much less reboots) but seems to be not fully compatible with older versions.**
|
||||
|
||||
* If you have problem with stalled systems you can try the following
|
||||
- Update the parameter `ImageQuality` to `12` instead of current value `5` (manually in the `config.ini`)
|
||||
|
||||
- If this is not helping, you might need to update your hardware or stay with version 9.2
|
||||
|
||||
##### 10.2.0 - Stability Increase (2022-01-14)
|
||||
|
||||
- Due to the updated camera driver, the image looks different and a new setup might be needed
|
||||
|
||||
- Update reference image
|
||||
- Update Alignment marks
|
||||
|
||||
- Reduce reboot due to camera problems
|
||||
|
||||
- Update esp32-camera to new version (master as of 2022-01-09)
|
||||
|
||||
|
||||
|
||||
##### 10.1.1 - Stability Increase (2022-01-12)
|
||||
|
||||
- Bug Fix MQTT problem
|
||||
- Issue:
|
||||
- Changing from v9.x to 10.x the MQTT-parameter "Topic" was renamed into "MainTopic" to address multiple number meters. This renaming should have been done automatically in the background within the graphical configuration, but was not working. Instead the parameter "Topic" was deleted and "MainTopic" was set to disabled and "undefined".
|
||||
- ToDo
|
||||
- Update the `html.zip`
|
||||
- If old `config.ini` available: copy it to `/config`, open the graphical configuration and save it again.
|
||||
- If old `config.ini` not available: reset the parameter "MainTopic" within the `config.ini` manually
|
||||
- Reboot
|
||||
|
||||
##### 10.1.0 - Stability Increase (2022-01-09)
|
||||
|
||||
- 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
|
||||
|
||||
|
||||
|
||||
##### 9.2.0 - External Illumination (2021-12-02)
|
||||
|
||||
- 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
|
||||
|
||||
##### 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 set 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
|
||||
|
||||
|
||||
|
||||
##### 8.5.0 - Multi Meter Support (2021-10-07)
|
||||
|
||||
@@ -11,18 +11,78 @@
|
||||
|
||||
____
|
||||
|
||||
#### #18 Document WLAN-strength in web page
|
||||
#### #29 Add favicon and use the hostname for the website
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/563
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/927
|
||||
|
||||
#### #28 Improved error handling for ROIs
|
||||
|
||||
* In case a ROI is out of the image, there is no error message, but a non sense image is used
|
||||
* Implement a error message for wrong configuratioin of ROI
|
||||
|
||||
#### #27 Use Homie Spec for Mqtt binding
|
||||
|
||||
* Use the standardized Home Protocol for the Mqtt binding
|
||||
* https://homieiot.github.io/
|
||||
|
||||
#### #26 Changes behaviour for "N" replacement
|
||||
|
||||
* in case the higher digits has already increased by minium 1 - don't set the "N" to the last value, but to "0"
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/792
|
||||
|
||||
|
||||
#### #25 Trigger Measurement via MQTT
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/727
|
||||
|
||||
|
||||
#### #24 Show Mqtt state directly in Webserver
|
||||
|
||||
* Show MQTT log in Web page. E.g. connection established or failed to connect...
|
||||
|
||||
|
||||
|
||||
|
||||
#### #23 CPU Temp and Mqtt values
|
||||
|
||||
* Show the CPU Temp directly in Webpage. Also add the value to MQTT sending
|
||||
|
||||
|
||||
|
||||
#### #22 Direct hint to the different neural network files in the other repositories
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/644
|
||||
|
||||
|
||||
|
||||
#### #21 Extended "CheckDigitalConsistency" Logik
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/590
|
||||
|
||||
|
||||
|
||||
#### #20 Deep sleep and push mode
|
||||
|
||||
* Let the device be normally in deep sleep state, and wake it up periodically to collect data and push it via MQTT or HTTP post.
|
||||
* Support ESP-NOW to reduce the overhead of connecting to wifi and mqtt
|
||||
* the above should enable battery powered applications
|
||||
|
||||
|
||||
#### #19 Extended log informations
|
||||
|
||||
* https://github.com/jomjol/AI-on-the-edge-device/issues/580
|
||||
|
||||
|
||||
|
||||
#### ~~#18 Document WLAN-strength in web page~~
|
||||
|
||||
* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/563~~
|
||||
|
||||
|
||||
|
||||
#### #17 Direct InfluxDB connection
|
||||
#### ~~#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
|
||||
* ~~Done in v10.6.0~~
|
||||
|
||||
|
||||
#### #16 Serial Communication
|
||||
@@ -64,9 +124,9 @@ ____
|
||||
|
||||
|
||||
|
||||
#### #12 Less reboots due to memory leakage
|
||||
#### ~~#12 Less reboots due to memory leakage~~
|
||||
|
||||
* Issue: #414 & #425 #430
|
||||
* ~~Issue: #414 & #425 #430~~
|
||||
|
||||
|
||||
|
||||
@@ -185,4 +245,4 @@ ____
|
||||
|
||||
* ~~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~~
|
||||
* ~~Adopt the configuration algorithm with a configurable light source~~
|
||||
|
||||
134
README.md
134
README.md
@@ -4,11 +4,10 @@ This is an example of Artificial Intelligence (AI) calculations on a very cheap
|
||||
|
||||
### Details on **function**, **installation** and **configuration** can be found on the **[Wiki Page](https://github.com/jomjol/AI-on-the-edge-device/wiki)**
|
||||
|
||||
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
|
||||
A 3d-printable housing can be found here:
|
||||
- https://www.thingiverse.com/thing:4573481 (Water Meter)
|
||||
- https://www.thingiverse.com/thing:5028229 (Power Meter)
|
||||
- https://www.thingiverse.com/thing:4571627 (ESP32-Cam housing only)
|
||||
|
||||
<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">
|
||||
|
||||
@@ -34,113 +33,58 @@ 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">
|
||||
|
||||
------
|
||||
## 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!
|
||||
**General remark:** Besides the file `firmware.bin`, typically the content of `/html` will need to be updated!
|
||||
|
||||
------
|
||||
|
||||
##### 11.2.0 - Intermediate Digits (2022-08-28)
|
||||
|
||||
- Updated Tensorflow / TFlite to newest tflite (version as of 2022-07-27)
|
||||
- Updated analog neural network file (`ana-cont_11.3.0_s2.tflite` - default, `ana-class100_0120_s1_q.tflite`)
|
||||
- Updated digital neural network file (`dig-cont_0570_s3.tflite` - default, `dig-class100_0120_s2_q.tflite`)
|
||||
|
||||
##### 10.2.0 - Stability Increase (2022-01-14)
|
||||
- Added automated filtering of tflite-file in the graphical configuration (thanks to @**[caco3](https://github.com/caco3)**)
|
||||
- Updated consistency algorithm & test cases
|
||||
- HTML: added favicon and system name, Improved reboot dialog (thanks to @**[caco3](https://github.com/caco3)**)
|
||||
|
||||
- **ATTENTION:** Due to the update camera driver, the image looks different and a new setup might be needed
|
||||
##### 11.1.1 - Intermediate Digits (2022-08-22)
|
||||
|
||||
- Update reference image
|
||||
- Update Alignment marks
|
||||
- New and improved consistency check (especially with analog and digital counters mixed)
|
||||
- Bug Fix: digital counter algorithm
|
||||
|
||||
- Reduce reboot due to camera problems
|
||||
##### 11.0.1 - Intermediate Digits (2022-08-18)
|
||||
|
||||
- Update esp32-camera to new version (master as of 2022-01-09)
|
||||
- **NEW v11.0.1**: Bug Fix InfluxDB configuration (only update of html.zip necessary)
|
||||
|
||||
- Implementation of new CNN types to detect intermediate values of digits with rolling numbers
|
||||
|
||||
- By default the old algo (0, 1, ..., 9, "N") is active (due to the limited types of digits trained so far)
|
||||
- Activation can be done by selection a tflite file with the new trained model in the 'config.ini'
|
||||
- **Details can be found in the [wiki](https://github.com/jomjol/AI-on-the-edge-device/wiki/Neural-Network-Types)** (different types, trained image types, naming convention)
|
||||
|
||||
- Updated neural network files (and adaption to new naming convention)
|
||||
|
||||
- Published a tool to download and combine log files - **Thanks to **
|
||||
|
||||
- Files see ['/tools/logfile-tool'](tbd), How-to see [wiki](https://github.com/jomjol/AI-on-the-edge-device/wiki/Gasmeter-Log-Downloader)
|
||||
|
||||
- Bug Fix: InfluxDB enabling in grahic configuration
|
||||
|
||||
|
||||
|
||||
##### 10.1.1 - Stability Increase (2022-01-12)
|
||||
## Tools
|
||||
|
||||
- Bug Fix MQTT problem
|
||||
- Issue:
|
||||
- Changing from v9.x to 10.x the MQTT-paramter "Topic" was renamed into "MainTopic" to address multiple number meters This renaming should have been done automatically in the background within the graphical configuration, but was not working. Instead the parameter "Topic" was deleted and "MainTopic" was set to disabled and "undefined".
|
||||
- ToDo
|
||||
- Update the `html.zip`
|
||||
- If old `config.ini` available: copy it to `/config`, open the graphical configuration and save it again.
|
||||
- If old `config.ini` not available: reset the parameter "MainTopic" within the `config.ini` manually
|
||||
- Reboot
|
||||
|
||||
##### 10.1.0 - Stability Increase (2022-01-09)
|
||||
|
||||
- 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
|
||||
|
||||
|
||||
|
||||
##### 9.2.0 - External Illumination (2021-12-02)
|
||||
|
||||
- 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
|
||||
|
||||
##### 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
|
||||
* Logfile downloader and combiner (Thx to [reserve85](https://github.com/reserve85))
|
||||
* Files see ['/tools/logfile-tool'](tbd), How-to see [wiki](https://github.com/jomjol/AI-on-the-edge-device/wiki/Gasmeter-Log-Downloader)
|
||||
|
||||
|
||||
|
||||
## Additional Ideas
|
||||
|
||||
|
||||
|
||||
## Additional ideas
|
||||
|
||||
There are some ideas and feature request, which are not followed currently - mainly due to capacity reasons on side of the developer. They are collected here: [FeatureRequest.md](FeatureRequest.md)
|
||||
There are some ideas and feature requests which are not followed currently - mainly due to capacity reasons on side of the developer. They are collected here: [FeatureRequest.md](FeatureRequest.md)
|
||||
|
||||
|
||||
|
||||
@@ -148,7 +92,11 @@ There are some ideas and feature request, which are not followed currently - mai
|
||||
|
||||
## History
|
||||
|
||||
##### 8.5.0 - Multi Meter Support (2021-10-07)
|
||||
##### 10.6.2 - Stability Increase (2022-07-24)
|
||||
|
||||
##### 9.2.0 - External Illumination (2021-12-02)
|
||||
|
||||
##### 8.5.0 Multi Meter Support (2021-10-07)
|
||||
|
||||
##### 7.1.2 MQTT-Update - (2021-06-17)
|
||||
|
||||
|
||||
57
code/components/esp-nn/.gitignore
vendored
Normal file
57
code/components/esp-nn/.gitignore
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
.config
|
||||
*.o
|
||||
*.i
|
||||
*.s
|
||||
*.orig
|
||||
*.pyc
|
||||
|
||||
# gtags
|
||||
GTAGS
|
||||
GRTAGS
|
||||
GPATH
|
||||
|
||||
# emacs
|
||||
.dir-locals.el
|
||||
|
||||
# emacs temp file suffixes
|
||||
*~
|
||||
.#*
|
||||
\#*#
|
||||
|
||||
# eclipse setting
|
||||
.settings
|
||||
|
||||
# MacOS directory files
|
||||
.DS_Store
|
||||
|
||||
# Example project files
|
||||
examples/**/sdkconfig
|
||||
examples/**/sdkconfig.old
|
||||
examples/**/build
|
||||
|
||||
# Test app files
|
||||
test_app/build
|
||||
test_app/sdkconfig
|
||||
test_app/sdkconfig.old
|
||||
|
||||
# Doc build artifacts
|
||||
docs/_build/
|
||||
docs/doxygen-warning-log.txt
|
||||
docs/sphinx-warning-log.txt
|
||||
docs/sphinx-warning-log-sanitized.txt
|
||||
docs/xml/
|
||||
docs/xml_in/
|
||||
docs/man/
|
||||
docs/doxygen_sqlite3.db
|
||||
|
||||
TEST_LOGS
|
||||
|
||||
|
||||
# gcov coverage reports
|
||||
*.gcda
|
||||
*.gcno
|
||||
coverage.info
|
||||
coverage_report/
|
||||
|
||||
# VS Code Settings
|
||||
.vscode/
|
||||
55
code/components/esp-nn/.gitlab-ci.yml
Normal file
55
code/components/esp-nn/.gitlab-ci.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
stages:
|
||||
- build
|
||||
|
||||
variables:
|
||||
BATCH_BUILD: "1"
|
||||
V: "0"
|
||||
MAKEFLAGS: "-j8 --no-keep-going"
|
||||
IDF_PATH: "$CI_PROJECT_DIR/esp-idf"
|
||||
LOG_PATH: "$CI_PROJECT_DIR"
|
||||
|
||||
.set_git_config: &set_git_config
|
||||
# Set git config
|
||||
- git config user.email "test@espressif.com"
|
||||
- git config user.name "Espressif"
|
||||
|
||||
.add_ssh_key: &add_ssh_key
|
||||
# Add gitlab ssh key
|
||||
- mkdir -p ~/.ssh
|
||||
- chmod 700 ~/.ssh
|
||||
- echo -n $GITLAB_KEY > ~/.ssh/id_rsa_base64
|
||||
- base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa
|
||||
- chmod 600 ~/.ssh/id_rsa
|
||||
- echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
|
||||
|
||||
before_script:
|
||||
# Add gitlab ssh key
|
||||
- *add_ssh_key
|
||||
# Set git config
|
||||
- *set_git_config
|
||||
|
||||
.build_esp32s3: &build_esp32s3
|
||||
- idf.py set-target esp32s3 build
|
||||
|
||||
.build_esp32: &build_esp32
|
||||
- idf.py set-target esp32 build
|
||||
|
||||
build_demo:
|
||||
stage: build
|
||||
image: $CI_DOCKER_REGISTRY/esp32-ci-env:esp-nn
|
||||
tags:
|
||||
- build
|
||||
script:
|
||||
# Clone IDF
|
||||
- git clone --recursive --single-branch -b release/v4.4 --reference-if-able /local_references/gitlab/ https://gitlab-ci-token:${BOT_TOKEN}@gitlab.espressif.cn:6688/espressif/esp-idf.git
|
||||
- cd esp-idf
|
||||
- ./install.sh
|
||||
- . ./export.sh
|
||||
- cd ..
|
||||
# Build examples now
|
||||
- cd test_app
|
||||
# Build esp32s3
|
||||
- *build_esp32s3
|
||||
# Build esp32
|
||||
- *build_esp32
|
||||
- cd -
|
||||
50
code/components/esp-nn/CMakeLists.txt
Normal file
50
code/components/esp-nn/CMakeLists.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
idf_build_get_property(idf_target IDF_TARGET)
|
||||
|
||||
set(c_srcs
|
||||
"src/activation_functions/esp_nn_relu_ansi.c"
|
||||
"src/basic_math/esp_nn_add_ansi.c"
|
||||
"src/basic_math/esp_nn_mul_ansi.c"
|
||||
"src/convolution/esp_nn_conv_ansi.c"
|
||||
"src/convolution/esp_nn_conv_opt.c"
|
||||
"src/convolution/esp_nn_depthwise_conv_ansi.c"
|
||||
"src/convolution/esp_nn_depthwise_conv_opt.c"
|
||||
"src/fully_connected/esp_nn_fully_connected_ansi.c"
|
||||
"src/softmax/esp_nn_softmax_ansi.c"
|
||||
"src/softmax/esp_nn_softmax_opt.c"
|
||||
"src/pooling/esp_nn_avg_pool_ansi.c"
|
||||
"src/pooling/esp_nn_max_pool_ansi.c")
|
||||
|
||||
if(CONFIG_IDF_TARGET_ESP32S3)
|
||||
set(s3_srcs
|
||||
"src/common/esp_nn_common_functions_esp32s3.S"
|
||||
"src/common/esp_nn_multiply_by_quantized_mult_esp32s3.S"
|
||||
"src/common/esp_nn_multiply_by_quantized_mult_ver1_esp32s3.S"
|
||||
"src/activation_functions/esp_nn_relu_s8_esp32s3.S"
|
||||
"src/basic_math/esp_nn_add_s8_esp32s3.S"
|
||||
"src/basic_math/esp_nn_mul_s8_esp32s3.S"
|
||||
"src/convolution/esp_nn_conv_esp32s3.c"
|
||||
"src/convolution/esp_nn_depthwise_conv_s8_esp32s3.c"
|
||||
"src/convolution/esp_nn_conv_s16_mult8_esp32s3.S"
|
||||
"src/convolution/esp_nn_conv_s8_mult8_1x1_esp32s3.S"
|
||||
"src/convolution/esp_nn_conv_s16_mult4_1x1_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult1_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult1_3x3_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult1_3x3_no_pad_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult8_3x3_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult4_esp32s3.S"
|
||||
"src/convolution/esp_nn_depthwise_conv_s16_mult8_esp32s3.S"
|
||||
"src/fully_connected/esp_nn_fully_connected_s8_esp32s3.S"
|
||||
"src/pooling/esp_nn_max_pool_s8_esp32s3.S"
|
||||
"src/pooling/esp_nn_avg_pool_s8_esp32s3.S")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "${c_srcs}"
|
||||
"${s3_srcs}"
|
||||
INCLUDE_DIRS "include" "src/common")
|
||||
|
||||
if(CONFIG_IDF_TARGET_ESP32S3)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -mlongcalls -fno-unroll-loops -O2 -Wno-unused-function)
|
||||
else()
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-function)
|
||||
endif()
|
||||
29
code/components/esp-nn/Kconfig.projbuild
Normal file
29
code/components/esp-nn/Kconfig.projbuild
Normal file
@@ -0,0 +1,29 @@
|
||||
menu "ESP-NN"
|
||||
|
||||
choice NN_OPTIMIZATIONS
|
||||
bool "Optimization for nn functions"
|
||||
default NN_OPTIMIZED
|
||||
help
|
||||
Use ANSI-C versions for verification and debug purpose.
|
||||
Optimisations are automatically picked up for a chipset.
|
||||
For ESP32-S3, assembly optimisations are selected.
|
||||
For other platforms(viz., ESP32, ESP32-C3), generic optimisations are used.
|
||||
|
||||
config NN_ANSI_C
|
||||
bool "ANSI C"
|
||||
help
|
||||
ANSI C versions for verification and debug purposes.
|
||||
config NN_OPTIMIZED
|
||||
bool "Optimized versions"
|
||||
help
|
||||
Optimisations are automatically picked up for a chipset.
|
||||
For ESP32-S3, assembly optimisations are selected.
|
||||
For other platforms(viz., ESP32, ESP32-C3), generic optimisations are used.
|
||||
endchoice
|
||||
|
||||
config NN_OPTIMIZATIONS
|
||||
int
|
||||
default 0 if NN_ANSI_C
|
||||
default 1 if NN_OPTIMIZED
|
||||
|
||||
endmenu
|
||||
202
code/components/esp-nn/LICENSE
Normal file
202
code/components/esp-nn/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
55
code/components/esp-nn/README.md
Normal file
55
code/components/esp-nn/README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# ESP-NN
|
||||
|
||||
The library contains optimised NN (Neural Network) functions for various Espressif chipsets.
|
||||
|
||||
* Supported platforms:
|
||||
* TensorFlow Lite Micro (TFLite Micro). Repo can be found [here](https://github.com/espressif/tflite-micro-esp-examples)
|
||||
|
||||
* Supported ESP chipsets include:
|
||||
* ESP32-S3 (Assembly versions optimised to benefit from vector instructions of ESP32-S3)
|
||||
* ESP32 (Generic optimisations)
|
||||
* ESP32-C3 (Generic optimisations)
|
||||
|
||||
## Performance
|
||||
|
||||
### Kernelwise performance for s8 versions:
|
||||
|
||||
* Kernelwise performance on ESP32-S3 chip
|
||||
* Numbers are ticks taken for kernel to execute
|
||||
* Chip config: 240MHz, SPI: QPI 80MHz, Data cache: 64KB
|
||||
|
||||
| Function | ANSI C | ESP32-S3 Opt | Opt Ratio | Data info | Memory |
|
||||
| ----------------| --------|---------|---------|-------------|-----------|
|
||||
| elementwise_add | 320397 | 87119 | 3.68 | size = 1615 | External |
|
||||
| elementwise_mul | 125958 | 44239 | 2.85 | size = 1615 | External |
|
||||
| convolution | 4663012 | 428675 | 10.88 | input(10,10), filter(64x1x1x64) | External |
|
||||
| convolution | 301014 | 32433 | 9.28 | input(8,8), filter(16x1x1x16) | External |
|
||||
| convolution | 2115418 | 1020923 | 2.07 | input(10,10), filter(64x3x3x3) | External |
|
||||
| depthwise conv | 1190062 | 203278 | 5.85 | input (18, 18), pad(0,0), stride(1,1) filter: 1x3x3x16 | External |
|
||||
| depthwise conv | 837072 | 182335 | 4.59 | input (12, 12), pad(1,1), stride(1,1) filter: 8x5x5x4 | External |
|
||||
| max pool | 485714 | 76747 | 6.33 | input(16,16), filter (1x3x3x16) | Internal |
|
||||
| avg pool | 541462 | 160580 | 3.37 | input(16,16), filter (1x3x3x16) | Internal |
|
||||
| fully connected | 15853 | 9547 | 1.66 | len: 265, ch = 3 | Internal |
|
||||
| prelu (relu6) | 19472 | 2734 | 7.12 | size, 1615 | Internal |
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
* To configure, please use `idf.py menuconfig` and under `ESP-NN` select `NN_OPTIMIZATIONS`
|
||||
* There are two options presented:
|
||||
* Optimized versions
|
||||
* ANSI C
|
||||
|
||||
* Default selection is for `Optimized versions`. For ESP32-S3, assembly versions are automatically selected, whereas for other chipsets (viz., ESP32, ESP32-C3), generic optimisations are selected.
|
||||
* For debugging purposes, you may want to select `ANSI C` reference versions.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
If you encounter an issue with ESP-NN, or wish to submit a feature request, please use the Issues section on the Github.
|
||||
|
||||
For general questions related to this library, please use the esp32.com forum.
|
||||
|
||||
## Copyrights and License
|
||||
|
||||
All original source code in this repository is Copyright (C) 2020-2021 Espressif Systems. This source code is licensed under the Apache License 2.0 as described in the file LICENSE.
|
||||
46
code/components/esp-nn/include/esp_nn.h
Normal file
46
code/components/esp-nn/include/esp_nn.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(CONFIG_NN_OPTIMIZED)
|
||||
// select apt optimisations
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
||||
#define ARCH_ESP32_S3 1
|
||||
#endif
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
#define ARCH_ESP32 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* reference kernels included by default */
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
#if defined(CONFIG_NN_OPTIMIZED)
|
||||
#if defined(ARCH_ESP32_S3)
|
||||
#include "esp_nn_esp32s3.h"
|
||||
#else // for other platforms use generic optimisations
|
||||
#include "esp_nn_generic_opt.h"
|
||||
#endif // #if defined(ARCH_ESP32_S3)
|
||||
#else
|
||||
#include "esp_nn_ansi_c.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
47
code/components/esp-nn/include/esp_nn_ansi_c.h
Normal file
47
code/components/esp-nn/include/esp_nn_ansi_c.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @file Header definitions to include for ANSI C versions.
|
||||
* These are just typedefs to pick up ANSI versions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
#define esp_nn_add_elementwise_s8 esp_nn_add_elementwise_s8_ansi
|
||||
#define esp_nn_mul_elementwise_s8 esp_nn_mul_elementwise_s8_ansi
|
||||
|
||||
#define esp_nn_depthwise_conv_s8 esp_nn_depthwise_conv_s8_ansi
|
||||
|
||||
#define esp_nn_conv_s8 esp_nn_conv_s8_ansi
|
||||
|
||||
#define esp_nn_get_conv_scratch_size esp_nn_get_conv_scratch_size_ansi
|
||||
#define esp_nn_set_conv_scratch_buf esp_nn_set_conv_scratch_buf_ansi
|
||||
|
||||
#define esp_nn_get_depthwise_conv_scratch_size esp_nn_get_depthwise_conv_scratch_size_ansi
|
||||
#define esp_nn_set_depthwise_conv_scratch_buf esp_nn_set_depthwise_conv_scratch_buf_ansi
|
||||
|
||||
#define esp_nn_relu6_s8 esp_nn_relu6_s8_ansi
|
||||
|
||||
#define esp_nn_avg_pool_s8 esp_nn_avg_pool_s8_ansi
|
||||
#define esp_nn_max_pool_s8 esp_nn_max_pool_s8_ansi
|
||||
|
||||
#define esp_nn_fully_connected_s8 esp_nn_fully_connected_s8_ansi
|
||||
|
||||
#define esp_nn_get_softmax_scratch_size esp_nn_get_softmax_scratch_size_ansi
|
||||
#define esp_nn_set_softmax_scratch_buf esp_nn_set_softmax_scratch_buf_ansi
|
||||
#define esp_nn_softmax_s8 esp_nn_softmax_s8_ansi
|
||||
309
code/components/esp-nn/include/esp_nn_ansi_headers.h
Normal file
309
code/components/esp-nn/include/esp_nn_ansi_headers.h
Normal file
@@ -0,0 +1,309 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file Header definitions to include for esp_nn reference functions
|
||||
*/
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
/************************** Basic math functions ****************************/
|
||||
|
||||
/**
|
||||
* @brief elementwise addition
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*
|
||||
* shift values are expected to be <= 0
|
||||
*/
|
||||
void esp_nn_add_elementwise_s8_ansi(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
const int32_t input1_mult,
|
||||
const int32_t input2_mult,
|
||||
const int32_t input1_shift,
|
||||
const int32_t input2_shift,
|
||||
const int32_t left_shift,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size);
|
||||
/**
|
||||
* @brief elementwise multiplication
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*
|
||||
* output shift is expected to be <= 0
|
||||
*/
|
||||
void esp_nn_mul_elementwise_s8_ansi(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size);
|
||||
|
||||
|
||||
/************************** Convolution functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief depthwise convolution per channel
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* Version used in tflite is per channel.
|
||||
* This version follows the same footsprints.
|
||||
* Meaning, it has per out_channel shift and multiplier for
|
||||
* requantization
|
||||
*
|
||||
* optimization notes: Though input_offset is int32 type,
|
||||
* offset values are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_depthwise_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
/**
|
||||
* @brief 2d-convolution channelwise
|
||||
*
|
||||
* @note operation: result += (input + offset) * filter
|
||||
*
|
||||
* inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
int esp_nn_get_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params);
|
||||
void esp_nn_set_conv_scratch_buf_ansi(const void *buf);
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params);
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_ansi(const void *buf);
|
||||
|
||||
/************************** Activation functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief relu6
|
||||
*
|
||||
* @note inout: int8_t
|
||||
*/
|
||||
void esp_nn_relu6_s8_ansi(int8_t *data, uint16_t size);
|
||||
|
||||
/************************** Pooling functions *****************************/
|
||||
|
||||
|
||||
/**
|
||||
* @brief max_pool
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_max_pool_s8_ansi(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels);
|
||||
|
||||
/**
|
||||
* @brief avg_pool
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_avg_pool_s8_ansi(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels);
|
||||
|
||||
|
||||
/************************** Fully connected functions ***********************/
|
||||
|
||||
/**
|
||||
* @brief fully connected
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_fully_connected_s8_ansi(const int8_t *input_data,
|
||||
const int32_t input_offset,
|
||||
const uint16_t row_len,
|
||||
const int8_t *filter_data,
|
||||
const int32_t filter_offset,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_shift,
|
||||
const int32_t out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
/**
|
||||
* @brief Get scratch buffer size needed by softmax function
|
||||
*
|
||||
* @param width
|
||||
* @param height
|
||||
* @return size in bytes
|
||||
*
|
||||
* @note buffer must be 4 byte aligned
|
||||
*/
|
||||
int32_t esp_nn_get_softmax_scratch_size_ansi(const int32_t width, const int32_t height);
|
||||
|
||||
/* ANSI C function to be hooked up when optimised version needed */
|
||||
int32_t esp_nn_get_softmax_scratch_size_opt(const int32_t width, const int32_t height);
|
||||
|
||||
/**
|
||||
* @brief Set scratch buffer to be used by softmax function
|
||||
*
|
||||
* @param buffer this can be NULL if one needs to unset it
|
||||
* must be aligned to 4 bytes
|
||||
*/
|
||||
void esp_nn_set_softmax_scratch_buf_ansi(void *buffer);
|
||||
|
||||
/**
|
||||
* @brief reference softmax function
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
*/
|
||||
void esp_nn_softmax_s8_ansi(const int8_t *input_data,
|
||||
const int32_t height,
|
||||
const int32_t width,
|
||||
const int32_t mult,
|
||||
const int32_t shift,
|
||||
const int32_t diff_min,
|
||||
int8_t *output_data);
|
||||
|
||||
|
||||
//////////////////////////// Generic optimisations /////////////////////////////
|
||||
|
||||
/************************** Convolution functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief 2d-convolution channelwise optimized version
|
||||
*
|
||||
* @note operation: result += (input + offset) * filter
|
||||
*
|
||||
* inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
/**
|
||||
* @brief depthwise convolution per channel optimized version
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* Version used in tflite is per channel.
|
||||
* This version follows the same footsprints.
|
||||
* Meaning, it has per out_channel shift and multiplier for
|
||||
* requantization
|
||||
*
|
||||
* optimization notes: Though input_offset is int32 type,
|
||||
* offset values are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_depthwise_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
int esp_nn_get_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params);
|
||||
void esp_nn_set_conv_scratch_buf_opt(const void *buf);
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params);
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_opt(const void *buf);
|
||||
|
||||
/* ANSI C function to be hooked up when optimised version needed */
|
||||
void esp_nn_set_softmax_scratch_buf_opt(void *buffer);
|
||||
|
||||
/**
|
||||
* @brief optimised version of softmax function
|
||||
*
|
||||
* @note the function uses extra buffer (4 * width bytes)
|
||||
* hence, scratch buffers must be set before calling this.
|
||||
*/
|
||||
void esp_nn_softmax_s8_opt(const int8_t *input_data,
|
||||
const int32_t height,
|
||||
const int32_t width,
|
||||
const int32_t mult,
|
||||
const int32_t shift,
|
||||
const int32_t diff_min,
|
||||
int8_t *output_data);
|
||||
83
code/components/esp-nn/include/esp_nn_defs.h
Normal file
83
code/components/esp-nn/include/esp_nn_defs.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief structure to club data dims
|
||||
* this structure can be used for input, output and filter
|
||||
*/
|
||||
typedef struct data_dims {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
int32_t channels;
|
||||
|
||||
int32_t extra; // can be used as batch or any other param
|
||||
} data_dims_t;
|
||||
|
||||
/**
|
||||
* @brief 2d data structure (width, height)
|
||||
*
|
||||
*/
|
||||
typedef struct data_2d {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
} data_2d_t;
|
||||
|
||||
/**
|
||||
* @brief min/max activation
|
||||
*/
|
||||
typedef struct act_params {
|
||||
int32_t min;
|
||||
int32_t max;
|
||||
} act_params_t;
|
||||
|
||||
/**
|
||||
* @brief per channel quant data
|
||||
*
|
||||
* @note number of shift and mult elements are equal to output channels
|
||||
*/
|
||||
typedef struct quant_data {
|
||||
int32_t *shift;
|
||||
int32_t *mult;
|
||||
} quant_data_t;
|
||||
|
||||
/**
|
||||
* @brief params specific to convolution 2d
|
||||
*
|
||||
*/
|
||||
typedef struct conv_params {
|
||||
int32_t in_offset;
|
||||
int32_t out_offset;
|
||||
data_2d_t stride;
|
||||
data_2d_t padding;
|
||||
data_2d_t dilation;
|
||||
act_params_t activation;
|
||||
} conv_params_t;
|
||||
|
||||
/**
|
||||
* @brief params specific to depthwise convolution 2d
|
||||
*
|
||||
*/
|
||||
typedef struct dw_conv_params {
|
||||
int32_t in_offset;
|
||||
int32_t out_offset;
|
||||
int32_t ch_mult; // channel multiplier. (in_ch * ch_mult = out_ch)
|
||||
data_2d_t stride;
|
||||
data_2d_t padding;
|
||||
data_2d_t dilation;
|
||||
act_params_t activation;
|
||||
} dw_conv_params_t;
|
||||
231
code/components/esp-nn/include/esp_nn_esp32s3.h
Normal file
231
code/components/esp-nn/include/esp_nn_esp32s3.h
Normal file
@@ -0,0 +1,231 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @file Header definitions to include for esp_nn optimized functions for
|
||||
* the ESP32-S3 platform
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
/************************** Basic math functions *****************************/
|
||||
|
||||
|
||||
/**
|
||||
* @brief elementwise addition
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*
|
||||
* shift values are expected to be <= 0
|
||||
*/
|
||||
void esp_nn_add_elementwise_s8_esp32s3(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
const int32_t input1_mult,
|
||||
const int32_t input2_mult,
|
||||
const int32_t input1_shift,
|
||||
const int32_t input2_shift,
|
||||
const int32_t left_shift,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size);
|
||||
|
||||
/**
|
||||
* @brief elementwise multiplication
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*
|
||||
* output shift is expected to be <= 0
|
||||
*/
|
||||
void esp_nn_mul_elementwise_s8_esp32s3(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size);
|
||||
|
||||
|
||||
/************************** Convolution functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief depthwise convolution per channel
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* Version used in tflite is per channel.
|
||||
* This version follows the same footsprints.
|
||||
* Meaning, it has per out_channel shift and multiplier for
|
||||
* requantization
|
||||
*
|
||||
* optimization notes: Though input_offset is int32 type,
|
||||
* offset values are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_depthwise_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *output_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
/**
|
||||
* @brief 2d - convolution channelwise
|
||||
*
|
||||
* @note operation: result += (input + offset) * filter
|
||||
*
|
||||
* inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *output_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data);
|
||||
|
||||
int esp_nn_get_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params);
|
||||
void esp_nn_set_conv_scratch_buf_esp32s3(const void *buf);
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params);
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_esp32s3(const void *buf);
|
||||
|
||||
/************************** Pooling functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief max_pool
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_max_pool_s8_esp32s3(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels);
|
||||
|
||||
/**
|
||||
* @brief avg_pool
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*/
|
||||
void esp_nn_avg_pool_s8_esp32s3(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels);
|
||||
|
||||
|
||||
/************************** Fully connected functions *****************************/
|
||||
|
||||
/**
|
||||
* @brief fully connected
|
||||
*
|
||||
* @note inputs type: int8_t, output: int8_t
|
||||
* input offsets: although int32_t, they are contained in 8 bits [-128, 127]
|
||||
*
|
||||
* Current version works only on aligned input.
|
||||
* row_len and channels should both be multiple of 8.
|
||||
*/
|
||||
void esp_nn_fully_connected_s8_esp32s3(const int8_t *input_data,
|
||||
const int32_t input_offset,
|
||||
const uint16_t row_len,
|
||||
const int8_t *filter_data,
|
||||
const int32_t filter_offset,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_shift,
|
||||
const int32_t out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
/**
|
||||
* @brief relu6
|
||||
*
|
||||
* @note inout: int8_t
|
||||
*/
|
||||
void esp_nn_relu6_s8_esp32s3(int8_t *data, uint16_t size);
|
||||
|
||||
/********************** function defines ***************************/
|
||||
|
||||
#define esp_nn_add_elementwise_s8 esp_nn_add_elementwise_s8_esp32s3
|
||||
#define esp_nn_mul_elementwise_s8 esp_nn_mul_elementwise_s8_esp32s3
|
||||
|
||||
#define esp_nn_depthwise_conv_s8 esp_nn_depthwise_conv_s8_esp32s3
|
||||
|
||||
#define esp_nn_get_conv_scratch_size esp_nn_get_conv_scratch_size_esp32s3
|
||||
#define esp_nn_set_conv_scratch_buf esp_nn_set_conv_scratch_buf_esp32s3
|
||||
|
||||
#define esp_nn_get_depthwise_conv_scratch_size esp_nn_get_depthwise_conv_scratch_size_esp32s3
|
||||
#define esp_nn_set_depthwise_conv_scratch_buf esp_nn_set_depthwise_conv_scratch_buf_esp32s3
|
||||
|
||||
#define esp_nn_conv_s8 esp_nn_conv_s8_esp32s3
|
||||
|
||||
#define esp_nn_relu6_s8 esp_nn_relu6_s8_esp32s3
|
||||
|
||||
#define esp_nn_avg_pool_s8 esp_nn_avg_pool_s8_esp32s3
|
||||
#define esp_nn_max_pool_s8 esp_nn_max_pool_s8_esp32s3
|
||||
|
||||
#define esp_nn_fully_connected_s8 esp_nn_fully_connected_s8_esp32s3
|
||||
|
||||
#define esp_nn_get_softmax_scratch_size esp_nn_get_softmax_scratch_size_opt
|
||||
#define esp_nn_set_softmax_scratch_buf esp_nn_set_softmax_scratch_buf_opt
|
||||
#define esp_nn_softmax_s8 esp_nn_softmax_s8_opt
|
||||
47
code/components/esp-nn/include/esp_nn_generic_opt.h
Normal file
47
code/components/esp-nn/include/esp_nn_generic_opt.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @file Header definitions to include for esp_nn generic optimisations
|
||||
* For functions which not having optimisations, _ansi versions are picked.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_nn_defs.h"
|
||||
#include "esp_nn_ansi_headers.h"
|
||||
|
||||
#define esp_nn_add_elementwise_s8 esp_nn_add_elementwise_s8_ansi
|
||||
#define esp_nn_mul_elementwise_s8 esp_nn_mul_elementwise_s8_ansi
|
||||
|
||||
#define esp_nn_depthwise_conv_s8 esp_nn_depthwise_conv_s8_opt
|
||||
|
||||
#define esp_nn_conv_s8 esp_nn_conv_s8_opt
|
||||
|
||||
#define esp_nn_get_conv_scratch_size esp_nn_get_conv_scratch_size_opt
|
||||
#define esp_nn_set_conv_scratch_buf esp_nn_set_conv_scratch_buf_opt
|
||||
|
||||
#define esp_nn_get_depthwise_conv_scratch_size esp_nn_get_depthwise_conv_scratch_size_opt
|
||||
#define esp_nn_set_depthwise_conv_scratch_buf esp_nn_set_depthwise_conv_scratch_buf_opt
|
||||
|
||||
#define esp_nn_relu6_s8 esp_nn_relu6_s8_ansi
|
||||
|
||||
#define esp_nn_avg_pool_s8 esp_nn_avg_pool_s8_ansi
|
||||
#define esp_nn_max_pool_s8 esp_nn_max_pool_s8_ansi
|
||||
|
||||
#define esp_nn_fully_connected_s8 esp_nn_fully_connected_s8_ansi
|
||||
|
||||
#define esp_nn_get_softmax_scratch_size esp_nn_get_softmax_scratch_size_opt
|
||||
#define esp_nn_set_softmax_scratch_buf esp_nn_set_softmax_scratch_buf_opt
|
||||
#define esp_nn_softmax_s8 esp_nn_softmax_s8_opt
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_relu6_s8_ansi(int8_t *data, uint16_t size)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
int32_t ip = data[i];
|
||||
|
||||
ip = max(ip, 0);
|
||||
data[i] = min(ip, 6);
|
||||
}
|
||||
}
|
||||
97
code/components/esp-nn/src/basic_math/esp_nn_add_ansi.c
Normal file
97
code/components/esp-nn/src/basic_math/esp_nn_add_ansi.c
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_add_elementwise_u8_ansi(const uint8_t *input1_data,
|
||||
const uint8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
const int32_t input1_mult,
|
||||
const int32_t input2_mult,
|
||||
const int32_t input1_shift,
|
||||
const int32_t input2_shift,
|
||||
const int32_t left_shift,
|
||||
uint8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size)
|
||||
{
|
||||
for (int i = 0; i < size; i++) {
|
||||
int32_t tmp1 = input1_data[i] + input1_offset;
|
||||
int32_t tmp2 = input2_data[i] + input2_offset;
|
||||
|
||||
tmp1 <<= left_shift;
|
||||
tmp2 <<= left_shift;
|
||||
|
||||
tmp1 = esp_nn_sat_round_doubling_high_mul(tmp1, input1_mult);
|
||||
tmp2 = esp_nn_sat_round_doubling_high_mul(tmp2, input2_mult);
|
||||
|
||||
tmp1 = esp_nn_div_by_power_of_two(tmp1, -input1_shift);
|
||||
tmp2 = esp_nn_div_by_power_of_two(tmp2, -input2_shift);
|
||||
|
||||
int32_t out = tmp1 + tmp2;
|
||||
out = esp_nn_sat_round_doubling_high_mul(out, out_mult);
|
||||
out = esp_nn_div_by_power_of_two(out, -out_shift);
|
||||
out = out + out_offset;
|
||||
|
||||
out = max(activation_min, min(out, activation_max));
|
||||
output[i] = (uint8_t) out;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_add_elementwise_s8_ansi(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
const int32_t input1_mult,
|
||||
const int32_t input2_mult,
|
||||
const int32_t input1_shift,
|
||||
const int32_t input2_shift,
|
||||
const int32_t left_shift,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size)
|
||||
{
|
||||
for (int i = 0; i < size; i++) {
|
||||
int32_t tmp1 = input1_data[i] + input1_offset;
|
||||
int32_t tmp2 = input2_data[i] + input2_offset;
|
||||
|
||||
tmp1 <<= left_shift;
|
||||
tmp2 <<= left_shift;
|
||||
|
||||
tmp1 = esp_nn_sat_round_doubling_high_mul(tmp1, input1_mult);
|
||||
tmp2 = esp_nn_sat_round_doubling_high_mul(tmp2, input2_mult);
|
||||
|
||||
tmp1 = esp_nn_div_by_power_of_two(tmp1, -input1_shift);
|
||||
tmp2 = esp_nn_div_by_power_of_two(tmp2, -input2_shift);
|
||||
|
||||
int32_t out = tmp1 + tmp2;
|
||||
out = esp_nn_sat_round_doubling_high_mul(out, out_mult);
|
||||
out = esp_nn_div_by_power_of_two(out, -out_shift);
|
||||
out = out + out_offset;
|
||||
|
||||
out = max(activation_min, min(out, activation_max));
|
||||
output[i] = (int8_t) out;
|
||||
}
|
||||
}
|
||||
42
code/components/esp-nn/src/basic_math/esp_nn_mul_ansi.c
Normal file
42
code/components/esp-nn/src/basic_math/esp_nn_mul_ansi.c
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_mul_elementwise_s8_ansi(const int8_t *input1_data,
|
||||
const int8_t *input2_data,
|
||||
const int32_t input1_offset,
|
||||
const int32_t input2_offset,
|
||||
int8_t *output,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_mult,
|
||||
const int32_t out_shift,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const int32_t size)
|
||||
{
|
||||
for (int i = 0; i < size; i++) {
|
||||
int32_t tmp1 = input1_data[i] + input1_offset;
|
||||
int32_t tmp2 = input2_data[i] + input2_offset;
|
||||
|
||||
int32_t out = tmp1 * tmp2;
|
||||
out = esp_nn_multiply_by_quantized_mult(out, out_mult, out_shift);
|
||||
out = out + out_offset;
|
||||
|
||||
out = max(activation_min, min(out, activation_max));
|
||||
output[i] = (int8_t) out;
|
||||
}
|
||||
}
|
||||
255
code/components/esp-nn/src/common/common_functions.h
Normal file
255
code/components/esp-nn/src/common/common_functions.h
Normal file
@@ -0,0 +1,255 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* c99 standard still doesn't strictly inline functions
|
||||
* We need to use attribute as well to do this.
|
||||
*/
|
||||
#define __NN_FORCE_INLINE__ __attribute((always_inline)) static inline
|
||||
|
||||
/* min/max macros */
|
||||
#ifndef max
|
||||
#define max(a, b) ({ \
|
||||
__typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
|
||||
#define min(a, b) ({ \
|
||||
__typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
#endif
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_clz32(uint32_t in)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
__asm__ volatile("nsau %0, %0" : "+r" (in));
|
||||
return in;
|
||||
#elif defined(__GNUC__)
|
||||
return __builtin_clz(in);
|
||||
#else
|
||||
int32_t count = 32;
|
||||
uint32_t x = in, y = in >> 16;
|
||||
if (y != 0) {
|
||||
count -= 16;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 8;
|
||||
if (y != 0) {
|
||||
count -= 8;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 4;
|
||||
if (y != 0) {
|
||||
count -= 4;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 2;
|
||||
if (y != 0) {
|
||||
count -= 2;
|
||||
x = y;
|
||||
}
|
||||
y = x >> 1;
|
||||
if (y != 0) {
|
||||
return count - 2;
|
||||
}
|
||||
return count - x;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Signed saturate a 32 bit value to 8 bits keeping output in 32 bit variable.
|
||||
*/
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_saturate8(int32_t in)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
__asm__ volatile("clamps %0, %0, 7" : "+a"(in));
|
||||
return in;
|
||||
#else
|
||||
return max(INT8_MIN, min(in, INT8_MAX));
|
||||
#endif
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_pick_sat_high32_of64(int64_t val64)
|
||||
{
|
||||
int32_t sign = (int32_t) (val64 >> 63);
|
||||
int32_t to_add = sign & ((1ul << 31) - 1);
|
||||
return (int32_t) ((int64_t) (val64 + to_add) >> 31);
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_sat_round_doubling_high_mul(int32_t in0, int32_t in1)
|
||||
{
|
||||
int32_t result;
|
||||
int64_t in0_64 = (int64_t) in0;
|
||||
bool overflow = (in0 == in1) && (in0 == (int32_t) INT32_MIN);
|
||||
|
||||
/* Nudge value */
|
||||
int64_t nudge_val = 1 << 30;
|
||||
if ((in0 < 0) ^ (in1 < 0)) {
|
||||
nudge_val = 1 - nudge_val;
|
||||
}
|
||||
|
||||
/* Multiply and add nudge */
|
||||
int64_t mult = in0_64 * in1 + nudge_val;
|
||||
|
||||
/* Round and pickup 32 bits */
|
||||
result = esp_nn_pick_sat_high32_of64(mult);
|
||||
|
||||
return overflow ? INT32_MAX : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* fast version
|
||||
* this will fail for values closer to INT32_MAX and INT32_MIN by `1 << (exponent - 1)`.
|
||||
* We can afford to do this because we are at the very last stage of filter.
|
||||
* Also it is pretty rare condition as our output is going to be 8 bit.
|
||||
*/
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_div_by_power_of_two_fast(int32_t val, int32_t exponent)
|
||||
{
|
||||
int32_t to_add = (1 << (exponent - 1)) - (val < 0);
|
||||
return (int32_t) ((val + to_add) >> exponent);
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_div_by_power_of_two(int32_t val, int32_t exponent)
|
||||
{
|
||||
int32_t result;
|
||||
|
||||
const int32_t mask = (1 << exponent) - 1;
|
||||
const int32_t remainder = val & mask;
|
||||
|
||||
result = val >> exponent;
|
||||
int32_t threshold = (mask >> 1) + (result < 0);
|
||||
|
||||
if (remainder > threshold) {
|
||||
result += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_multiply_by_quantized_mult(int32_t x, int32_t mult, int32_t shift)
|
||||
{
|
||||
int32_t left_shift = shift > 0 ? shift : 0;
|
||||
int32_t right_shift = shift > 0 ? 0 : -shift;
|
||||
int32_t result = esp_nn_sat_round_doubling_high_mul(x * (1 << left_shift), mult);
|
||||
return esp_nn_div_by_power_of_two(result, right_shift);
|
||||
}
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_multiply_by_quantized_mult_fast(int32_t x, int32_t mult, int32_t shift)
|
||||
{
|
||||
int32_t left_shift = max(shift, 0);
|
||||
int32_t right_shift = left_shift - shift;
|
||||
|
||||
int64_t nudge_val = 1 << 30;
|
||||
int64_t in0_64 = (int64_t) (x << left_shift);
|
||||
|
||||
/* Multiply and add nudge */
|
||||
int64_t mult_64 = in0_64 * mult + nudge_val;
|
||||
int32_t result = (int32_t) (mult_64 >> 31);
|
||||
if (right_shift) {
|
||||
result = esp_nn_div_by_power_of_two_fast(result, right_shift);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void esp_nn_aligned_s8_pad_with_value(const int8_t *src, int8_t *dst,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t pad_val,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht)
|
||||
{
|
||||
/* memset with pad_val */
|
||||
memset(dst, pad_val, ((input_wd + 2 * pad_wd) * (input_ht + 2 * pad_ht)) * channels);
|
||||
dst += (pad_wd + input_wd + pad_wd) * channels;
|
||||
|
||||
for (int i = 0; i < input_ht; i++) {
|
||||
dst += pad_wd * channels;
|
||||
for (int j = 0; j < input_wd * channels; j++) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
dst += pad_wd * channels;
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_nn_aligned_s8_pad_end_with_value(const int8_t *src, int8_t *dst,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t pad_val,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht)
|
||||
{
|
||||
for (int i = 0; i < input_ht; i++) {
|
||||
for (int j = 0; j < input_wd * channels; j++) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
if (pad_wd) {
|
||||
memset(dst, pad_val, pad_wd * channels);
|
||||
dst += pad_wd * channels;
|
||||
}
|
||||
}
|
||||
/* pad end `pad_ht` lines at end */
|
||||
if (pad_ht) {
|
||||
memset(dst, pad_val, (input_wd + pad_wd) * pad_ht * channels);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief convert 8 bit input data to 16 bit
|
||||
*
|
||||
* @param src int8_t source data
|
||||
* @param dst int16_t dst data
|
||||
* @param size length of data
|
||||
* @param offset offset to be added to src data. Range: [-128, 127]
|
||||
*/
|
||||
__NN_FORCE_INLINE__ void esp_nn_s8_to_s16_with_offset(const int8_t *src, int16_t *dst,
|
||||
const int size, const int32_t offset)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < size; i += 2) {
|
||||
dst[i + 0] = src[i + 0] + offset;
|
||||
dst[i + 1] = src[i + 1] + offset;
|
||||
}
|
||||
if(i < size) {
|
||||
dst[i] = src[i] + offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief convert 8 bit input data to 16 bit
|
||||
*
|
||||
* @param src int8_t source data
|
||||
* @param dst int16_t dst data
|
||||
* @param size length of data
|
||||
*/
|
||||
__NN_FORCE_INLINE__ void esp_nn_s8_to_s16(const int8_t *src, int16_t *dst, const int size)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < size; i += 2) {
|
||||
dst[i + 0] = src[i + 0];
|
||||
dst[i + 1] = src[i + 1];
|
||||
}
|
||||
if(i < size) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
179
code/components/esp-nn/src/convolution/esp_nn_conv_ansi.c
Normal file
179
code/components/esp-nn/src/convolution/esp_nn_conv_ansi.c
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <esp_nn_defs.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_conv_scratch_buf_ansi(const void *buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumption 1: i/p channels == o/p channels
|
||||
* Assumption 2: Pointers are valid
|
||||
* Assumption 3: dialation width = 1
|
||||
*/
|
||||
void esp_nn_conv_u8_ansi(const uint8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t filter_offset,
|
||||
const int32_t *bias,
|
||||
uint8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_shift,
|
||||
const int32_t out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
for (int out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {//channel_loop
|
||||
int32_t result = 0;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
for (int in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * in_channels + in_ch_idx;
|
||||
int32_t filter_index = ((out_ch_idx * filter_ht + filter_y_idx)
|
||||
* filter_wd + filter_x_idx) * in_channels
|
||||
+ in_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index] + filter_offset;
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[out_ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult(result, out_mult, out_shift);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
int out_index = (out_y * out_wd + out_x) * out_channels + out_ch_idx;
|
||||
out_data[out_index] = (uint8_t) result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumption 1: i/p channels == o/p channels
|
||||
* Assumption 2: Pointers are valid
|
||||
* Assumption 3: dialation width = 1
|
||||
*/
|
||||
void esp_nn_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int32_t base_y = stride_ht * out_y - pad_ht;
|
||||
const int32_t base_x = stride_wd * out_x - pad_wd;
|
||||
|
||||
const int32_t filter_y_start = max(0, -base_y);
|
||||
const int32_t filter_x_start = max(0, -base_x);
|
||||
|
||||
const int32_t filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
const int32_t filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
for (filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
int32_t input_base_offset = (in_row * input_wd + in_col) * in_channels;
|
||||
int32_t filter_base_offset = out_ch_idx * in_channels * filter_ht * filter_wd +
|
||||
(filter_y_idx * filter_wd + filter_x_idx) * in_channels;
|
||||
for (in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
|
||||
conv_out +=
|
||||
(input_data[input_base_offset + in_ch_idx] + input_offset) *
|
||||
filter_data[filter_base_offset + in_ch_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
463
code/components/esp-nn/src/convolution/esp_nn_conv_esp32s3.c
Normal file
463
code/components/esp-nn/src/convolution/esp_nn_conv_esp32s3.c
Normal file
@@ -0,0 +1,463 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdio.h>
|
||||
#include <esp_nn_defs.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
static int16_t *scratch_buffer = NULL;
|
||||
|
||||
extern void esp_nn_conv_s8_mult8_1x1_esp32s3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const int8_t *filter_aligned,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
void *buffer /* scratch buffer */);
|
||||
|
||||
extern void esp_nn_conv_s16_mult4_1x1_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int16_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
void *buffer /* scratch buffer */);
|
||||
|
||||
extern void esp_nn_conv_s16_mult8_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int16_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_aligned_s8_to_s16_with_offset_esp32s3(const int8_t *src, int16_t *dst,
|
||||
const int size, const int32_t offset);
|
||||
|
||||
extern void esp_nn_s8_to_s16_esp32s3(const int8_t *src, int16_t *dst, const int size);
|
||||
|
||||
static void esp_nn_conv_s8_unrolled(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_ch = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_ch = output_dims->channels;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
for (out_ch_idx = 0; out_ch_idx < out_ch; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int32_t base_y = stride_ht * out_y - pad_ht;
|
||||
const int32_t base_x = stride_wd * out_x - pad_wd;
|
||||
|
||||
const int32_t filter_y_start = max(0, -base_y);
|
||||
const int32_t filter_x_start = max(0, -base_x);
|
||||
|
||||
const int32_t filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
const int32_t filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
for (filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
int32_t input_base_offset = (in_row * input_wd + in_col) * in_ch;
|
||||
int32_t filter_base_offset = out_ch_idx * in_ch * filter_ht * filter_wd +
|
||||
(filter_y_idx * filter_wd + filter_x_idx) * in_ch;
|
||||
for (in_ch_idx = 0; in_ch_idx < in_ch; in_ch_idx++) {
|
||||
conv_out +=
|
||||
(input_data[input_base_offset + in_ch_idx] + input_offset) *
|
||||
filter_data[filter_base_offset + in_ch_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_nn_conv_s8_pad_valid(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int32_t base_y = stride_ht * out_y;
|
||||
const int32_t base_x = stride_wd * out_x;
|
||||
|
||||
for (filter_y_idx = 0; filter_y_idx < filter_ht; filter_y_idx++) {
|
||||
for (filter_x_idx = 0; filter_x_idx < filter_wd; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
int32_t input_base_offset = (in_row * input_wd + in_col) * in_channels;
|
||||
int32_t filter_base_offset = out_ch_idx * in_channels * filter_ht * filter_wd +
|
||||
(filter_y_idx * filter_wd + filter_x_idx) * in_channels;
|
||||
const int8_t *input_data_ptr = input_data + input_base_offset;
|
||||
const int8_t *filter_data_ptr = filter_data + filter_base_offset;
|
||||
for (in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
|
||||
conv_out += (*input_data_ptr++ + input_offset) * *filter_data_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_nn_conv_s8_pad_valid_3x3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t in_channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
const int32_t base_y = stride_ht * out_y;
|
||||
const int32_t base_x = stride_wd * out_x;
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
for (filter_y_idx = 0; filter_y_idx < 3; filter_y_idx++) {
|
||||
for (filter_x_idx = 0; filter_x_idx < 3; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
int32_t input_base_offset = (in_row * input_wd + in_col) * in_channels;
|
||||
int32_t filter_base_offset = out_ch_idx * in_channels * 3 * 3 +
|
||||
(filter_y_idx * 3 + filter_x_idx) * in_channels;
|
||||
const int8_t *input_data_ptr = input_data + input_base_offset;
|
||||
const int8_t *filter_data_ptr = filter_data + filter_base_offset;
|
||||
for (in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
|
||||
conv_out += (*input_data_ptr++ + input_offset) * *filter_data_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_nn_conv_s8_pad_valid_ch3_3x3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const int32_t input_offset,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
int32_t out_ch_idx, out_y, out_x, filter_y_idx;
|
||||
|
||||
/* use scratch_buffer to pre-compute offset factor */
|
||||
int16_t *filter_sum = (int16_t *) scratch_buffer;
|
||||
const int8_t *filter_ptr = filter_data;
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int16_t sum_val = 0;
|
||||
for (int i = 0; i < 9; i++) {
|
||||
sum_val += *filter_ptr++;
|
||||
sum_val += *filter_ptr++;
|
||||
sum_val += *filter_ptr++;
|
||||
}
|
||||
*filter_sum++ = sum_val;
|
||||
}
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
const int8_t *filter_data_ptr = filter_data;
|
||||
const int32_t base_y = stride_ht * out_y;
|
||||
const int32_t base_x = stride_wd * out_x;
|
||||
const int8_t *input_base_ptr = input_data + (base_y * input_wd + base_x) * 3;
|
||||
int16_t *filter_sum = (int16_t *) scratch_buffer;
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
for (filter_y_idx = 0; filter_y_idx < 3; filter_y_idx++) {
|
||||
const int8_t *input_data_ptr = input_base_ptr + (filter_y_idx * input_wd) * 3;
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
|
||||
}
|
||||
|
||||
conv_out += *filter_sum++ * input_offset;
|
||||
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int esp_nn_get_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_ch = input_dims->channels;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_ch = output_dims->channels;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * in_ch * out_ch;
|
||||
int input_size = input_wd * input_ht * in_ch;
|
||||
|
||||
int transpose_buf_size = 2 * (8 * in_ch); /* to store intermediate data */
|
||||
if (input_wd * input_ht < 8) {
|
||||
transpose_buf_size = 0; // not using this for leftover
|
||||
}
|
||||
int align_buf_size = 32; /* extra buffer for alignment */
|
||||
if (in_ch % 8 == 0 && filter_wd == 1 && filter_ht == 1 &&
|
||||
pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
|
||||
return filter_size + transpose_buf_size + align_buf_size;
|
||||
}
|
||||
return 2 * (filter_size + input_size) + transpose_buf_size + align_buf_size;
|
||||
}
|
||||
|
||||
void esp_nn_set_conv_scratch_buf_esp32s3(void *buf)
|
||||
{
|
||||
scratch_buffer = (int16_t *) buf;
|
||||
}
|
||||
|
||||
void esp_nn_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * channels * out_channels;
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
int align_len = 16 - (filter_size & 15);
|
||||
int16_t *filter_data16 = scratch_buffer;
|
||||
int16_t *input_data16 = scratch_buffer + filter_size + align_len;
|
||||
|
||||
if (scratch_buffer == NULL) {
|
||||
printf("esp_nn_conv error! scratch_buffer not set!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (channels % 8 == 0 && filter_wd == 1 && filter_ht == 1 &&
|
||||
pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
|
||||
int8_t *filter_aligned = (int8_t *) scratch_buffer;
|
||||
int scratch_offset = (int) (filter_aligned + filter_size);
|
||||
void *scratch_buf = (void *) (scratch_offset + 16 - (scratch_offset & 15));
|
||||
memcpy(filter_aligned, filter_data, filter_size); // copy to aligned address
|
||||
esp_nn_conv_s8_mult8_1x1_esp32s3(
|
||||
input, input_wd, input_ht, channels, input_offset, filter_aligned,
|
||||
bias, out_data, out_wd, out_ht, out_channels, out_offset,
|
||||
out_shift, out_mult, activation_min, activation_max, scratch_buf);
|
||||
} else if (channels % 4 == 0 && filter_wd == 1 && filter_ht == 1 &&
|
||||
(input_wd * input_ht) % 4 == 0 && /* TODO: remove this check */
|
||||
pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
|
||||
int scratch_offset = (int) (input_data16 + input_size);
|
||||
void *scratch_buf = (void *) (scratch_offset + 16 - (scratch_offset & 15));
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input, input_data16, input_size, input_offset);
|
||||
esp_nn_conv_s16_mult4_1x1_esp32s3(
|
||||
input_data16, input_wd, input_ht, channels, filter_data16,
|
||||
bias, out_data, out_wd, out_ht, out_channels, out_offset,
|
||||
out_shift, out_mult, activation_min, activation_max, scratch_buf);
|
||||
} else if (channels % 8 == 0) {
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input, input_data16, input_size, input_offset);
|
||||
esp_nn_conv_s16_mult8_esp32s3(
|
||||
input_data16, input_wd, input_ht, channels, pad_wd, pad_ht,
|
||||
stride_wd, stride_ht, filter_data16, filter_wd, filter_ht, bias,
|
||||
out_data, out_wd, out_ht, out_channels, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else if (pad_wd == 0 && pad_ht == 0) {
|
||||
if (filter_wd == 3 && filter_ht == 3 && channels == 3) {
|
||||
esp_nn_conv_s8_pad_valid_ch3_3x3(input, input_wd, input_ht, input_offset,
|
||||
stride_wd, stride_ht, filter_data, bias,
|
||||
out_data, out_wd, out_ht, out_channels, out_offset,
|
||||
out_shift, out_mult, activation_min, activation_max);
|
||||
} else {
|
||||
esp_nn_conv_s8_pad_valid(input, input_wd, input_ht, channels, input_offset,
|
||||
stride_wd, stride_ht, filter_data, filter_wd, filter_ht, bias,
|
||||
out_data, out_wd, out_ht, out_channels, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
}
|
||||
} else {
|
||||
/* Basic unrolled version */
|
||||
esp_nn_conv_s8_unrolled(input_dims, input, filter_dims, filter_data,
|
||||
bias, output_dims, out_data, conv_params, quant_data);
|
||||
}
|
||||
}
|
||||
179
code/components/esp-nn/src/convolution/esp_nn_conv_opt.c
Normal file
179
code/components/esp-nn/src/convolution/esp_nn_conv_opt.c
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <esp_nn_defs.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const conv_params_t *conv_params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_conv_scratch_buf_opt(const void *buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
__attribute__ ((noinline))
|
||||
static void esp_nn_conv_s8_1x1(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t in_channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
for (int32_t in_row = 0; in_row < out_ht * stride_ht; in_row += stride_ht) {
|
||||
for (int32_t in_col = 0; in_col < out_wd * stride_wd; in_col += stride_wd) {
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int8_t *filter_ptr = filter_data;
|
||||
const int8_t *input_base_ptr = input_data + (in_row * input_wd + in_col) * in_channels;
|
||||
int32_t out_ch_idx = 0;
|
||||
for (; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int8_t *input_ptr = input_base_ptr;
|
||||
|
||||
int32_t in_ch_idx = 0;
|
||||
for (; in_ch_idx < in_channels - 3; in_ch_idx += 4) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
for (; in_ch_idx < in_channels; in_ch_idx ++) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, *out_mult++, *out_shift++);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumption 1: i/p channels == o/p channels
|
||||
* Assumption 2: Pointers are valid
|
||||
* Assumption 3: dialation width = 1
|
||||
*/
|
||||
void esp_nn_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
|
||||
if (filter_wd == 1 && filter_ht == 1) {
|
||||
esp_nn_conv_s8_1x1(input_dims, input_data, filter_data, bias,
|
||||
output_dims, out_data, conv_params, quant_data);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t in_channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t out_channels = output_dims->channels;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int32_t out_ch_idx, out_y, out_x, filter_y_idx, filter_x_idx;
|
||||
|
||||
for (out_y = 0; out_y < out_ht; out_y++) {
|
||||
for (out_x = 0; out_x < out_wd; out_x++) {
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
|
||||
int32_t conv_out = 0;
|
||||
|
||||
const int32_t base_y = stride_ht * out_y - pad_ht;
|
||||
const int32_t base_x = stride_wd * out_x - pad_wd;
|
||||
|
||||
const int32_t filter_y_start = max(0, -base_y);
|
||||
const int32_t filter_x_start = max(0, -base_x);
|
||||
|
||||
const int32_t filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
const int32_t filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
for (filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t in_row = base_y + filter_y_idx;
|
||||
const int32_t in_col = base_x + filter_x_idx;
|
||||
|
||||
const int8_t *input_ptr = input_data +
|
||||
(in_row * input_wd + in_col) * in_channels;
|
||||
const int8_t *filter_ptr = filter_data +
|
||||
out_ch_idx * in_channels * filter_ht * filter_wd +
|
||||
(filter_y_idx * filter_wd + filter_x_idx) * in_channels;
|
||||
int32_t in_ch_idx = 0;
|
||||
for (; in_ch_idx < in_channels - 3; in_ch_idx += 4) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
for (; in_ch_idx < in_channels; in_ch_idx ++) {
|
||||
conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
conv_out += bias[out_ch_idx];
|
||||
}
|
||||
conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, *out_mult++, *out_shift++);
|
||||
conv_out += out_offset;
|
||||
conv_out = max(conv_out, activation_min);
|
||||
conv_out = min(conv_out, activation_max);
|
||||
*out_data++ = (int8_t) conv_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <esp_nn_defs.h>
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_ansi(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_ansi(const void *buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void esp_nn_depthwise_conv_s8_ansi(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
for (int ch_mult_idx = 0; ch_mult_idx < ch_mult; ch_mult_idx++) {
|
||||
int32_t result = 0;
|
||||
const int out_ch_idx = ch_mult_idx + ch_idx * ch_mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[out_ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult(result, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <esp_nn_defs.h>
|
||||
#include <common_functions.h>
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_opt(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_opt(const void *buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* common channel multiplier == 1 case */
|
||||
__attribute__ ((noinline))
|
||||
static void esp_nn_depthwise_conv_s8_ch_mult_1(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
int ch_idx = 0;
|
||||
for (; ch_idx < channels - 3; ch_idx += 4) {//channel_loop
|
||||
int32_t result0 = 0;
|
||||
int32_t result1 = 0;
|
||||
int32_t result2 = 0;
|
||||
int32_t result3 = 0;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels) + ch_idx;
|
||||
int32_t input_val0 = input_data[input_index + 0] + input_offset;
|
||||
int32_t input_val1 = input_data[input_index + 1] + input_offset;
|
||||
int32_t input_val2 = input_data[input_index + 2] + input_offset;
|
||||
int32_t input_val3 = input_data[input_index + 3] + input_offset;
|
||||
int32_t filter_val0 = filter_data[filter_index + 0];
|
||||
int32_t filter_val1 = filter_data[filter_index + 1];
|
||||
int32_t filter_val2 = filter_data[filter_index + 2];
|
||||
int32_t filter_val3 = filter_data[filter_index + 3];
|
||||
result0 += input_val0 * filter_val0;
|
||||
result1 += input_val1 * filter_val1;
|
||||
result2 += input_val2 * filter_val2;
|
||||
result3 += input_val3 * filter_val3;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result0 += bias[ch_idx + 0];
|
||||
result1 += bias[ch_idx + 1];
|
||||
result2 += bias[ch_idx + 2];
|
||||
result3 += bias[ch_idx + 3];
|
||||
}
|
||||
result0 = esp_nn_multiply_by_quantized_mult_fast(result0, *out_mult++, *out_shift++);
|
||||
result1 = esp_nn_multiply_by_quantized_mult_fast(result1, *out_mult++, *out_shift++);
|
||||
result2 = esp_nn_multiply_by_quantized_mult_fast(result2, *out_mult++, *out_shift++);
|
||||
result3 = esp_nn_multiply_by_quantized_mult_fast(result3, *out_mult++, *out_shift++);
|
||||
|
||||
result0 += out_offset;
|
||||
result1 += out_offset;
|
||||
result2 += out_offset;
|
||||
result3 += out_offset;
|
||||
|
||||
result0 = max(result0, activation_min);
|
||||
result1 = max(result1, activation_min);
|
||||
result2 = max(result2, activation_min);
|
||||
result3 = max(result3, activation_min);
|
||||
|
||||
result0 = min(result0, activation_max);
|
||||
result1 = min(result1, activation_max);
|
||||
result2 = min(result2, activation_max);
|
||||
result3 = min(result3, activation_max);
|
||||
|
||||
out_data[out_idx++] = result0;
|
||||
out_data[out_idx++] = result1;
|
||||
out_data[out_idx++] = result2;
|
||||
out_data[out_idx++] = result3;
|
||||
}
|
||||
for (; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
int32_t result = 0;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels) + ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult_fast(result, *out_mult++, *out_shift++);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_depthwise_conv_s8_opt(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
if (ch_mult == 1) {
|
||||
esp_nn_depthwise_conv_s8_ch_mult_1(input_dims, input_data, filter_dims, filter_data,
|
||||
bias, output_dims, out_data, conv_params, quant_data);
|
||||
return;
|
||||
}
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
int ch_mult_idx = 0;
|
||||
for (; ch_mult_idx < ch_mult - 3; ch_mult_idx += 4) {
|
||||
int32_t result0 = 0;
|
||||
int32_t result1 = 0;
|
||||
int32_t result2 = 0;
|
||||
int32_t result3 = 0;
|
||||
const int out_ch_idx = ch_idx * ch_mult + ch_mult_idx;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val0 = filter_data[filter_index + 0];
|
||||
int32_t filter_val1 = filter_data[filter_index + 1];
|
||||
int32_t filter_val2 = filter_data[filter_index + 2];
|
||||
int32_t filter_val3 = filter_data[filter_index + 3];
|
||||
result0 += input_val * filter_val0;
|
||||
result1 += input_val * filter_val1;
|
||||
result2 += input_val * filter_val2;
|
||||
result3 += input_val * filter_val3;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result0 += bias[out_ch_idx + 0];
|
||||
result1 += bias[out_ch_idx + 1];
|
||||
result2 += bias[out_ch_idx + 2];
|
||||
result3 += bias[out_ch_idx + 3];
|
||||
}
|
||||
result0 = esp_nn_multiply_by_quantized_mult_fast(result0, *out_mult++, *out_shift++);
|
||||
result1 = esp_nn_multiply_by_quantized_mult_fast(result1, *out_mult++, *out_shift++);
|
||||
result2 = esp_nn_multiply_by_quantized_mult_fast(result2, *out_mult++, *out_shift++);
|
||||
result3 = esp_nn_multiply_by_quantized_mult_fast(result3, *out_mult++, *out_shift++);
|
||||
|
||||
result0 += out_offset;
|
||||
result1 += out_offset;
|
||||
result2 += out_offset;
|
||||
result3 += out_offset;
|
||||
|
||||
result0 = max(result0, activation_min);
|
||||
result1 = max(result1, activation_min);
|
||||
result2 = max(result2, activation_min);
|
||||
result3 = max(result3, activation_min);
|
||||
result0 = min(result0, activation_max);
|
||||
result1 = min(result1, activation_max);
|
||||
result2 = min(result2, activation_max);
|
||||
result3 = min(result3, activation_max);
|
||||
|
||||
out_data[out_idx++] = result0;
|
||||
out_data[out_idx++] = result1;
|
||||
out_data[out_idx++] = result2;
|
||||
out_data[out_idx++] = result3;
|
||||
}
|
||||
for (; ch_mult_idx < ch_mult; ch_mult_idx++) {
|
||||
int32_t result = 0;
|
||||
const int out_ch_idx = ch_idx * ch_mult + ch_mult_idx;
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[out_ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult_fast(result, *out_mult++, *out_shift++);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,543 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdio.h>
|
||||
#include <esp_nn_defs.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
static int16_t *scratch_buffer = NULL;
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult8_3x3_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int16_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult1_3x3_no_pad_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int16_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult8_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int16_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult4_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int16_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult1_3x3_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int16_t *filter_data,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_depthwise_conv_s16_mult1_esp32s3(const int16_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int16_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max);
|
||||
|
||||
extern void esp_nn_s8_to_s16_esp32s3(const int8_t *src, int16_t *dst, const int size);
|
||||
|
||||
extern void esp_nn_aligned_s8_to_s16_with_offset_esp32s3(const int8_t *src, int16_t *dst,
|
||||
const int size, const int32_t offset);
|
||||
|
||||
static void esp_nn_depthwise_conv_s8_unrolled(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t ch_mult,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
int ch_mult_idx = 0;
|
||||
for (; ch_mult_idx < ch_mult - 3; ch_mult_idx += 4) {
|
||||
int32_t result0 = 0, result1 = 0, result2 = 0, result3 = 0;
|
||||
const int out_ch_idx = ch_mult_idx + ch_idx * ch_mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val0 = filter_data[filter_index + 0];
|
||||
int32_t filter_val1 = filter_data[filter_index + 1];
|
||||
int32_t filter_val2 = filter_data[filter_index + 2];
|
||||
int32_t filter_val3 = filter_data[filter_index + 3];
|
||||
result0 += input_val * filter_val0;
|
||||
result1 += input_val * filter_val1;
|
||||
result2 += input_val * filter_val2;
|
||||
result3 += input_val * filter_val3;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result0 += bias[out_ch_idx + 0];
|
||||
result1 += bias[out_ch_idx + 1];
|
||||
result2 += bias[out_ch_idx + 2];
|
||||
result3 += bias[out_ch_idx + 3];
|
||||
}
|
||||
result0 = esp_nn_multiply_by_quantized_mult(result0,
|
||||
out_mult[out_ch_idx + 0], out_shift[out_ch_idx + 0]);
|
||||
result1 = esp_nn_multiply_by_quantized_mult(result1,
|
||||
out_mult[out_ch_idx + 1], out_shift[out_ch_idx + 1]);
|
||||
result2 = esp_nn_multiply_by_quantized_mult(result2,
|
||||
out_mult[out_ch_idx + 2], out_shift[out_ch_idx + 2]);
|
||||
result3 = esp_nn_multiply_by_quantized_mult(result3,
|
||||
out_mult[out_ch_idx + 3], out_shift[out_ch_idx + 3]);
|
||||
|
||||
result0 += out_offset;
|
||||
result1 += out_offset;
|
||||
result2 += out_offset;
|
||||
result3 += out_offset;
|
||||
|
||||
result0 = max(result0, activation_min);
|
||||
result1 = max(result1, activation_min);
|
||||
result2 = max(result2, activation_min);
|
||||
result3 = max(result3, activation_min);
|
||||
|
||||
result0 = min(result0, activation_max);
|
||||
result1 = min(result1, activation_max);
|
||||
result2 = min(result2, activation_max);
|
||||
result3 = min(result3, activation_max);
|
||||
|
||||
out_data[out_idx++] = result0;
|
||||
out_data[out_idx++] = result1;
|
||||
out_data[out_idx++] = result2;
|
||||
out_data[out_idx++] = result3;
|
||||
}
|
||||
|
||||
/* left-over */
|
||||
for (; ch_mult_idx < ch_mult; ch_mult_idx++) {
|
||||
int32_t result = 0;
|
||||
const int out_ch_idx = ch_mult_idx + ch_idx * ch_mult;
|
||||
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[out_ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult(result, out_mult[out_ch_idx], out_shift[out_ch_idx]);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_depthwise_conv_s8_ch_mult1(const int8_t *input_data,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
const uint16_t channels,
|
||||
const int32_t input_offset,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const int8_t *filter_data,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_wd,
|
||||
const uint16_t out_ht,
|
||||
const int32_t out_offset,
|
||||
const int32_t *out_shift,
|
||||
const int32_t *out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
int out_idx = 0;
|
||||
for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
|
||||
const int16_t base_y = (out_y * stride_ht) - pad_ht;
|
||||
for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
|
||||
const int16_t base_x = (out_x * stride_wd) - pad_wd;
|
||||
for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
|
||||
int32_t result = 0;
|
||||
/* Select filter so as the point doesn't lie outside block */
|
||||
int filter_y_start = max(0, -base_y);
|
||||
int filter_x_start = max(0, -base_x);
|
||||
int filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
|
||||
const int32_t idx_y = base_y + filter_y_idx;
|
||||
for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
|
||||
const int32_t idx_x = base_x + filter_x_idx;
|
||||
int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
|
||||
int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * channels + ch_idx;
|
||||
int32_t input_val = input_data[input_index] + input_offset;
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += input_val * filter_val;
|
||||
}
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[ch_idx];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult(result, out_mult[ch_idx], out_shift[ch_idx]);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
out_data[out_idx++] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int esp_nn_get_depthwise_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
|
||||
const data_dims_t *filter_dims,
|
||||
const data_dims_t *output_dims,
|
||||
const dw_conv_params_t *conv_params)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * channels * ch_mult;
|
||||
int pad_width = 0, pad_height = 0;
|
||||
|
||||
if ((ch_mult == 1) && (channels % 8 == 0) && (filter_wd == 3) && (filter_ht == 3)) {
|
||||
if (channels % 16 == 0) {
|
||||
if (pad_wd || pad_ht) {
|
||||
pad_width = pad_wd * 2;
|
||||
pad_height = pad_ht * 2;
|
||||
} else {
|
||||
// check if we need to pad additionally
|
||||
pad_width = (out_wd * stride_wd + filter_wd - 1) - input_wd;
|
||||
pad_height = (out_ht * stride_ht + filter_ht - 1) - input_ht;
|
||||
// printf("in(%d %d %d), out(%d %d), filter (%d %d) stride (%d %d), pad (%d %d)",
|
||||
// input_wd, input_ht, channels, out_wd, out_ht, filter_wd, filter_ht,
|
||||
// stride_wd, stride_ht, pad_wd, pad_ht);
|
||||
}
|
||||
if (pad_width || pad_height) {
|
||||
int input_size = (input_wd + pad_width) * (input_ht + pad_height) * channels;
|
||||
// printf("ask1 %d\n", filter_size + input_size + 16);
|
||||
return filter_size + input_size + 16; // 16 for alignment
|
||||
} else {
|
||||
// printf("ask2 %d\n", filter_size + 16);
|
||||
return filter_size + 16; // 16 for alignment
|
||||
}
|
||||
} else {
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
// printf("ask3 %d\n", 2 * (filter_size + input_size) + 16);
|
||||
return 2 * (filter_size + input_size) + 16; // 16 for alignment
|
||||
}
|
||||
} else if (ch_mult % 4 == 0) {
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
// printf("ask4 %d\n", 2 * (filter_size + input_size) + 16);
|
||||
return 2 * (filter_size + input_size) + 16; // 16 for alignment
|
||||
}
|
||||
return 32; // just few bytes
|
||||
}
|
||||
|
||||
void esp_nn_set_depthwise_conv_scratch_buf_esp32s3(void *buf)
|
||||
{
|
||||
scratch_buffer = (int16_t *) buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumption 1: i/p channels == o/p channels
|
||||
* Assumption 2: Pointers are valid
|
||||
* Assumption 3: dialation width = 1
|
||||
*/
|
||||
|
||||
|
||||
|
||||
void esp_nn_depthwise_conv_s8_esp32s3(const data_dims_t *input_dims,
|
||||
const int8_t *input_data,
|
||||
const data_dims_t *filter_dims,
|
||||
const int8_t *filter_data,
|
||||
const int32_t *bias,
|
||||
const data_dims_t *output_dims,
|
||||
int8_t *out_data,
|
||||
const dw_conv_params_t *conv_params,
|
||||
const quant_data_t *quant_data)
|
||||
{
|
||||
const uint16_t input_wd = input_dims->width;
|
||||
const uint16_t input_ht = input_dims->height;
|
||||
const uint16_t channels = input_dims->channels;
|
||||
const int32_t input_offset = conv_params->in_offset;
|
||||
const int32_t out_offset = conv_params->out_offset;
|
||||
const uint16_t pad_wd = conv_params->padding.width;
|
||||
const uint16_t pad_ht = conv_params->padding.height;
|
||||
const uint16_t stride_wd = conv_params->stride.width;
|
||||
const uint16_t stride_ht = conv_params->stride.height;
|
||||
const uint16_t filter_wd = filter_dims->width;
|
||||
const uint16_t filter_ht = filter_dims->height;
|
||||
const uint16_t out_wd = output_dims->width;
|
||||
const uint16_t out_ht = output_dims->height;
|
||||
const int32_t *out_shift = quant_data->shift;
|
||||
const int32_t *out_mult = quant_data->mult;
|
||||
const int32_t activation_min = conv_params->activation.min;
|
||||
const int32_t activation_max = conv_params->activation.max;
|
||||
const uint16_t ch_mult = conv_params->ch_mult;
|
||||
|
||||
int filter_size = filter_wd * filter_ht * channels * ch_mult;
|
||||
int align_len = 16 - (filter_size & 15);
|
||||
int input_size = input_wd * input_ht * channels;
|
||||
int16_t *filter_data16 = scratch_buffer;
|
||||
int16_t *input_data16 = scratch_buffer + filter_size + align_len;
|
||||
if (scratch_buffer == NULL) {
|
||||
printf("esp_nn_depthwise_conv error! scratch_buffer not set!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ch_mult == 1) && (channels % 8 == 0)) {
|
||||
if ((filter_wd == 3) && (filter_ht == 3)) {
|
||||
if ((channels % 16 == 0) && (pad_wd == 1) && (pad_ht == 1)) {
|
||||
/* process in 8 bits */
|
||||
int8_t *filter_aligned = (int8_t *) scratch_buffer;
|
||||
int8_t *input_padded = (int8_t *) scratch_buffer + filter_size + align_len;
|
||||
memcpy(filter_aligned, filter_data, filter_size);
|
||||
esp_nn_aligned_s8_pad_with_value(input_data, input_padded, input_wd, input_ht, channels,
|
||||
-input_offset, pad_wd, pad_ht);
|
||||
esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(input_padded, input_wd + 2 * pad_wd,
|
||||
input_ht + 2 * pad_ht, channels, input_offset,
|
||||
stride_wd, stride_ht, filter_aligned, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else if ((channels % 16 == 0) && (pad_wd == 0) && (pad_ht == 0)) {
|
||||
/* process in 8 bits */
|
||||
int8_t *filter_aligned = (int8_t *) scratch_buffer;
|
||||
int8_t *input_padded = (int8_t *) scratch_buffer + filter_size + align_len;
|
||||
|
||||
// check if we need to pad additionally
|
||||
int pad_right = (out_wd * stride_wd + filter_wd - 1) - input_wd;
|
||||
int pad_bottom = (out_ht * stride_ht + filter_ht - 1) - input_ht;
|
||||
if (pad_right || pad_bottom) { // pad right and bottom
|
||||
esp_nn_aligned_s8_pad_end_with_value(input_data, input_padded, input_wd, input_ht,
|
||||
channels, -input_offset, pad_right, pad_bottom);
|
||||
} else {
|
||||
input_padded = (int8_t *) input_data;
|
||||
}
|
||||
memcpy(filter_aligned, filter_data, filter_size);
|
||||
esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(input_padded, input_wd + pad_right,
|
||||
input_ht + pad_bottom, channels, input_offset,
|
||||
stride_wd, stride_ht, filter_aligned, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else { /* (channels % 8) == 0 */
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input_data, input_data16, input_size, input_offset);
|
||||
esp_nn_depthwise_conv_s16_mult1_3x3_esp32s3(input_data16, input_wd, input_ht, channels,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, filter_data16,
|
||||
bias, out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
}
|
||||
} else { // all other ch_mult == 1, `channels % 8 == 0`
|
||||
esp_nn_depthwise_conv_s8_ch_mult1(input_data, input_wd, input_ht, channels, input_offset,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht,
|
||||
filter_data, filter_wd, filter_ht,
|
||||
bias, out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
}
|
||||
} else if (ch_mult % 8 == 0) {
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input_data, input_data16, input_size, input_offset);
|
||||
if (filter_wd == 3 && filter_ht == 3) {
|
||||
esp_nn_depthwise_conv_s16_mult8_3x3_esp32s3(input_data16, input_wd, input_ht, channels,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
|
||||
filter_data16, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else {
|
||||
esp_nn_depthwise_conv_s16_mult8_esp32s3(input_data16, input_wd, input_ht, channels,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
|
||||
filter_data16, filter_wd, filter_ht, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
}
|
||||
} else if (ch_mult % 4 == 0) {
|
||||
esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
|
||||
esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input_data, input_data16, input_size, input_offset);
|
||||
esp_nn_depthwise_conv_s16_mult4_esp32s3(input_data16, input_wd, input_ht, channels,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
|
||||
filter_data16, filter_wd, filter_ht, bias,
|
||||
out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
} else {
|
||||
esp_nn_depthwise_conv_s8_unrolled(input_data, input_wd, input_ht, channels, input_offset,
|
||||
pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
|
||||
filter_data, filter_wd, filter_ht,
|
||||
bias, out_data, out_wd, out_ht, out_offset, out_shift,
|
||||
out_mult, activation_min, activation_max);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_fully_connected_s8_ansi(const int8_t *input_data,
|
||||
const int32_t input_offset,
|
||||
const uint16_t row_len,
|
||||
const int8_t *filter_data,
|
||||
const int32_t filter_offset,
|
||||
const int32_t *bias,
|
||||
int8_t *out_data,
|
||||
const uint16_t out_channels,
|
||||
const int32_t out_offset,
|
||||
const int32_t out_shift,
|
||||
const int32_t out_mult,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max)
|
||||
{
|
||||
for (int32_t out_c = 0; out_c < out_channels; ++out_c) {
|
||||
int32_t result = 0;
|
||||
for (int32_t data_idx = 0; data_idx < row_len; data_idx++) {
|
||||
int32_t filter_index = row_len * out_c + data_idx;
|
||||
int32_t input_val = input_data[data_idx];
|
||||
int32_t filter_val = filter_data[filter_index];
|
||||
result += (filter_val + filter_offset) * (input_val + input_offset);
|
||||
}
|
||||
if (bias) {
|
||||
result += bias[out_c];
|
||||
}
|
||||
result = esp_nn_multiply_by_quantized_mult(result, out_mult, out_shift);
|
||||
result += out_offset;
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
out_data[out_c] = (int8_t) result;
|
||||
}
|
||||
}
|
||||
72
code/components/esp-nn/src/pooling/esp_nn_avg_pool_ansi.c
Normal file
72
code/components/esp-nn/src/pooling/esp_nn_avg_pool_ansi.c
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_avg_pool_s8_ansi(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels)
|
||||
{
|
||||
int32_t base_y = -pad_ht;
|
||||
for (int32_t out_y = 0; out_y < output_ht; out_y++, base_y += stride_ht) {
|
||||
int32_t base_x = -pad_wd;
|
||||
for (int32_t out_x = 0; out_x < output_wd; out_x++, base_x += stride_wd) {
|
||||
for (int32_t ch_idx = 0; ch_idx < channels; ch_idx++) {
|
||||
int32_t result = 0;
|
||||
int32_t filter_cnt = 0;
|
||||
/* Make sure filter does not cross the input box */
|
||||
int32_t filter_y_start = max(0, -base_y);
|
||||
int32_t filter_x_start = max(0, -base_x);
|
||||
|
||||
int32_t filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int32_t filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int32_t filter_y = filter_y_start; filter_y < filter_y_end; filter_y++) {
|
||||
for (int32_t filter_x = filter_x_start; filter_x < filter_x_end; filter_x++) {
|
||||
int32_t in_x_idx = base_x + filter_x;
|
||||
int32_t in_y_idx = base_y + filter_y;
|
||||
int32_t input_index = (in_y_idx * input_wd + in_x_idx) * channels + ch_idx;
|
||||
result += input[input_index];
|
||||
filter_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Rounded average */
|
||||
result = result > 0 ? (result + filter_cnt / 2) / filter_cnt
|
||||
: (result - filter_cnt / 2) / filter_cnt;
|
||||
|
||||
/* Activation function */
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
int32_t output_index = (out_y * output_wd + out_x) * channels + ch_idx;
|
||||
output[output_index] = (int8_t) result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
66
code/components/esp-nn/src/pooling/esp_nn_max_pool_ansi.c
Normal file
66
code/components/esp-nn/src/pooling/esp_nn_max_pool_ansi.c
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
|
||||
void esp_nn_max_pool_s8_ansi(const int8_t *input,
|
||||
const uint16_t input_wd,
|
||||
const uint16_t input_ht,
|
||||
int8_t *output,
|
||||
const uint16_t output_wd,
|
||||
const uint16_t output_ht,
|
||||
const uint16_t stride_wd,
|
||||
const uint16_t stride_ht,
|
||||
const uint16_t filter_wd,
|
||||
const uint16_t filter_ht,
|
||||
const uint16_t pad_wd,
|
||||
const uint16_t pad_ht,
|
||||
const int32_t activation_min,
|
||||
const int32_t activation_max,
|
||||
const uint16_t channels)
|
||||
{
|
||||
int32_t base_y = -pad_ht;
|
||||
for (int32_t out_y = 0; out_y < output_ht; out_y++, base_y += stride_ht) {
|
||||
int32_t base_x = -pad_wd;
|
||||
for (int32_t out_x = 0; out_x < output_wd; out_x++, base_x += stride_wd) {
|
||||
/* Make sure filter does not cross the input box */
|
||||
int32_t filter_y_start = max(0, -base_y);
|
||||
int32_t filter_x_start = max(0, -base_x);
|
||||
int32_t filter_y_end = min(filter_ht, input_ht - base_y);
|
||||
int32_t filter_x_end = min(filter_wd, input_wd - base_x);
|
||||
|
||||
for (int32_t ch_idx = 0; ch_idx < channels; ch_idx++) {
|
||||
int8_t result = INT8_MIN;
|
||||
|
||||
for (int32_t filter_y = filter_y_start; filter_y < filter_y_end; filter_y++) {
|
||||
for (int32_t filter_x = filter_x_start; filter_x < filter_x_end; filter_x++) {
|
||||
int32_t in_x_idx = base_x + filter_x;
|
||||
int32_t in_y_idx = base_y + filter_y;
|
||||
int32_t input_index = (in_y_idx * input_wd + in_x_idx) * channels + ch_idx;
|
||||
result = max(input[input_index], result);
|
||||
}
|
||||
}
|
||||
|
||||
/* Activation function */
|
||||
result = max(result, activation_min);
|
||||
result = min(result, activation_max);
|
||||
|
||||
int32_t output_index = (out_y * output_wd + out_x) * channels + ch_idx;
|
||||
output[output_index] = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
code/components/esp-nn/src/softmax/esp_nn_softmax_ansi.c
Normal file
88
code/components/esp-nn/src/softmax/esp_nn_softmax_ansi.c
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 "softmax_common.h"
|
||||
|
||||
int32_t esp_nn_get_softmax_scratch_size_ansi(const int32_t width, const int32_t height)
|
||||
{
|
||||
(void) width;
|
||||
(void) height;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esp_nn_set_softmax_scratch_buf_ansi(void *buffer)
|
||||
{
|
||||
(void) buffer;
|
||||
return;
|
||||
}
|
||||
|
||||
void esp_nn_softmax_s8_ansi(const int8_t *input_data,
|
||||
const int32_t height,
|
||||
const int32_t width,
|
||||
const int32_t mult,
|
||||
const int32_t shift,
|
||||
const int32_t diff_min,
|
||||
int8_t *output_data)
|
||||
{
|
||||
// The representation chosen for the input to the exp() function is Q5.26.
|
||||
// We need to leave extra space since values that we skip might be as large as
|
||||
// -32 before multiplying by input mult, and therefore as large as
|
||||
// -16 afterwards. Note that exp(-8) is definitely not insignificant to
|
||||
// accumulation, but exp(-16) definitely is.
|
||||
#define ACCUM_BITS 12
|
||||
#define DIFF_BITS 5
|
||||
|
||||
const int32_t mask = (1 << shift);
|
||||
int32_t col = 0;
|
||||
const int8_t *in_ptr = input_data;
|
||||
int8_t *out_ptr = output_data;
|
||||
|
||||
for (int row_idx = 0; row_idx < height; row_idx++) {
|
||||
int8_t max_in_row = in_ptr[0];
|
||||
for (col = 1; col < width; col++) {
|
||||
max_in_row = max(max_in_row, in_ptr[col]);
|
||||
}
|
||||
|
||||
int32_t input_diff = 0;
|
||||
int32_t sum_of_exps = 0;
|
||||
|
||||
for (col = 0; col < width; col++) {
|
||||
input_diff = in_ptr[col] - max_in_row;
|
||||
if (input_diff >= diff_min) {
|
||||
const int32_t input_diff_rescaled = SAT_HIGH_MUL(input_diff * mask, mult);
|
||||
const int32_t exp_raw = esp_nn_exp_on_negative_values(input_diff_rescaled);
|
||||
sum_of_exps += DIV_POW2(exp_raw, ACCUM_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
const int32_t headroom_plus1 = esp_nn_clz32((uint32_t) sum_of_exps);
|
||||
const int32_t shifted_scale = ONE_OVER_ONE_X((sum_of_exps << headroom_plus1) - (1 << 31));
|
||||
const int32_t bits_over_unit = ACCUM_BITS - headroom_plus1 + 31 - sizeof(int8_t) * 8;
|
||||
|
||||
for (col = 0; col < width; col++) {
|
||||
input_diff = in_ptr[col] - max_in_row;
|
||||
if (input_diff >= diff_min) {
|
||||
const int32_t input_diff_rescaled = SAT_HIGH_MUL(input_diff * mask, mult);
|
||||
const int32_t exp_raw = esp_nn_exp_on_negative_values(input_diff_rescaled);
|
||||
const int32_t shifted_output = SAT_HIGH_MUL(shifted_scale, exp_raw);
|
||||
const int32_t result = DIV_POW2(shifted_output, bits_over_unit) - 128;
|
||||
out_ptr[col] = (int8_t) esp_nn_saturate8(result);
|
||||
} else {
|
||||
out_ptr[col] = -128;
|
||||
}
|
||||
}
|
||||
in_ptr += width;
|
||||
out_ptr += width;
|
||||
}
|
||||
}
|
||||
108
code/components/esp-nn/src/softmax/esp_nn_softmax_opt.c
Normal file
108
code/components/esp-nn/src/softmax/esp_nn_softmax_opt.c
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 "softmax_common.h"
|
||||
#include <stdio.h>
|
||||
|
||||
static int32_t *scratch_buf = NULL;
|
||||
|
||||
/**
|
||||
* @brief Get scratch buffer size needed by softmax function
|
||||
*
|
||||
* @param width
|
||||
* @param height
|
||||
* @return size in bytes
|
||||
*
|
||||
* @note buffer must be 4 byte aligned
|
||||
*/
|
||||
int32_t esp_nn_get_softmax_scratch_size_opt(const int32_t width, const int32_t height)
|
||||
{
|
||||
(void) height;
|
||||
return width * 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set scratch buffer to be used by softmax function
|
||||
*
|
||||
* @param buffer this can be NULL if one needs to unset it
|
||||
* must be aligned to 4 bytes
|
||||
*/
|
||||
void esp_nn_set_softmax_scratch_buf_opt(void *buffer)
|
||||
{
|
||||
scratch_buf = (int32_t *) buffer;
|
||||
}
|
||||
|
||||
void esp_nn_softmax_s8_opt(const int8_t *input_data,
|
||||
const int32_t height,
|
||||
const int32_t width,
|
||||
const int32_t mult,
|
||||
const int32_t shift,
|
||||
const int32_t diff_min,
|
||||
int8_t *output_data)
|
||||
{
|
||||
if (scratch_buf == NULL) {
|
||||
printf("%s error! scratch buffer not set\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
// The representation chosen for the input to the exp() function is Q5.26.
|
||||
// We need to leave extra space since values that we skip might be as large as
|
||||
// -32 before multiplying by input mult, and therefore as large as
|
||||
// -16 afterwards. Note that exp(-8) is definitely not insignificant to
|
||||
// accumulation, but exp(-16) definitely is.
|
||||
#define ACCUM_BITS 12
|
||||
#define DIFF_BITS 5
|
||||
|
||||
const int32_t mask = (1 << shift);
|
||||
int32_t col = 0;
|
||||
const int8_t *in_ptr = input_data;
|
||||
int8_t *out_ptr = output_data;
|
||||
|
||||
for (int row_idx = 0; row_idx < height; row_idx++) {
|
||||
int8_t max_in_row = in_ptr[0];
|
||||
for (col = 1; col < width; col++) {
|
||||
max_in_row = max(max_in_row, in_ptr[col]);
|
||||
}
|
||||
|
||||
int32_t input_diff = 0;
|
||||
int32_t sum_of_exps = 0;
|
||||
|
||||
for (col = 0; col < width; col++) {
|
||||
input_diff = in_ptr[col] - max_in_row;
|
||||
if (input_diff >= diff_min) {
|
||||
const int32_t input_diff_rescaled = SAT_HIGH_MUL(input_diff * mask, mult);
|
||||
const int32_t exp_raw = esp_nn_exp_on_negative_values(input_diff_rescaled);
|
||||
scratch_buf[col] = exp_raw; // store to avoid duplicate calculation later
|
||||
sum_of_exps += DIV_POW2(exp_raw, ACCUM_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
const int32_t headroom_plus1 = esp_nn_clz32((uint32_t) sum_of_exps);
|
||||
const int32_t shifted_scale = ONE_OVER_ONE_X((sum_of_exps << headroom_plus1) - (1 << 31));
|
||||
const int32_t bits_over_unit = ACCUM_BITS - headroom_plus1 + 31 - sizeof(int8_t) * 8;
|
||||
|
||||
for (col = 0; col < width; col++) {
|
||||
input_diff = in_ptr[col] - max_in_row;
|
||||
if (input_diff >= diff_min) {
|
||||
int32_t exp_raw = scratch_buf[col];
|
||||
const int32_t shifted_output = SAT_HIGH_MUL(shifted_scale, exp_raw);
|
||||
const int32_t result = DIV_POW2(shifted_output, bits_over_unit) - 128;
|
||||
out_ptr[col] = (int8_t) esp_nn_saturate8(result);
|
||||
} else {
|
||||
out_ptr[col] = -128;
|
||||
}
|
||||
}
|
||||
in_ptr += width;
|
||||
out_ptr += width;
|
||||
}
|
||||
}
|
||||
104
code/components/esp-nn/src/softmax/softmax_common.h
Normal file
104
code/components/esp-nn/src/softmax/softmax_common.h
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
#include <common_functions.h>
|
||||
|
||||
#define MASK_IF_ZERO(x) (x) == 0 ? ~0 : 0
|
||||
#define MASK_IF_NON_ZERO(x) (x) != 0 ? ~0 : 0
|
||||
#define SELECT_USING_MASK(mask, a, b) ((mask) & (a)) ^ (~(mask) & (b))
|
||||
#define SAT_HIGH_MUL(x, y) esp_nn_sat_round_doubling_high_mul((x), (y))
|
||||
#define DIV_POW2(x,y) esp_nn_div_by_power_of_two((x), (y))
|
||||
|
||||
__NN_FORCE_INLINE__ int32_t mul_power_of_2(int val, int exp)
|
||||
{
|
||||
const int32_t thresh = ((1 << (31 - exp)) - 1);
|
||||
int32_t result = val << exp;
|
||||
result = SELECT_USING_MASK(MASK_IF_NON_ZERO(val > thresh), INT32_MAX, result);
|
||||
result = SELECT_USING_MASK(MASK_IF_NON_ZERO(val < -thresh), INT32_MIN, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate `1 / (1 + x)` for x in [0, 1]
|
||||
*
|
||||
* @param val input value to calculate `1/(1+x)` for
|
||||
* @return `int32_t` result
|
||||
* @note Newton-Raphson division
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Division_algorithm#Newton.E2.80.93Raphson_division
|
||||
* Refer to that page for the logic behind the 48/17 and 32/17 constants.
|
||||
* Pseudocode: https://en.wikipedia.org/wiki/Division_algorithm#Pseudocode
|
||||
*/
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_one_over_one_plus_x_for_x_in_0_1(int32_t val)
|
||||
{
|
||||
const int64_t sum = (int64_t) val + INT32_MAX;
|
||||
const int32_t half_denominator = (int32_t) ((sum + (sum >= 0 ? 1 : -1)) / 2L);
|
||||
int32_t constant_48_over_17 = 1515870810;
|
||||
int32_t constant_neg_32_over_17 = -1010580540;
|
||||
int32_t x = constant_48_over_17 + SAT_HIGH_MUL(half_denominator, constant_neg_32_over_17);
|
||||
const int32_t fixed_2_one = (1 << 29);
|
||||
|
||||
x += mul_power_of_2(SAT_HIGH_MUL(x, fixed_2_one - SAT_HIGH_MUL(half_denominator, x)), 2);
|
||||
x += mul_power_of_2(SAT_HIGH_MUL(x, fixed_2_one - SAT_HIGH_MUL(half_denominator, x)), 2);
|
||||
x += mul_power_of_2(SAT_HIGH_MUL(x, fixed_2_one - SAT_HIGH_MUL(half_denominator, x)), 2);
|
||||
|
||||
return mul_power_of_2(x, 1);
|
||||
}
|
||||
|
||||
#define ONE_OVER_ONE_X(x) esp_nn_one_over_one_plus_x_for_x_in_0_1((x))
|
||||
|
||||
/**
|
||||
* @brief Return exp(x) for x < 0.
|
||||
*
|
||||
*/
|
||||
__NN_FORCE_INLINE__ int32_t esp_nn_exp_on_negative_values(int32_t val)
|
||||
{
|
||||
int32_t shift = 24;
|
||||
|
||||
const int32_t one_quarter = (1 << shift);
|
||||
int32_t mask = one_quarter - 1;
|
||||
const int32_t val_mod_minus_quarter = (val & mask) - one_quarter;
|
||||
const int32_t remainder = val_mod_minus_quarter - val;
|
||||
|
||||
// calculate exponent for x in [-1/4, 0) in `result`
|
||||
const int32_t x = (val_mod_minus_quarter << 5) + (1 << 28);
|
||||
const int32_t x2 = SAT_HIGH_MUL(x, x);
|
||||
const int32_t x3 = SAT_HIGH_MUL(x2, x);
|
||||
const int32_t x4 = SAT_HIGH_MUL(x2, x2);
|
||||
const int32_t one_over_3 = 715827883;
|
||||
const int32_t one_over_8 = 1895147668;
|
||||
|
||||
const int32_t x4_over_4 = DIV_POW2(x4, 2);
|
||||
const int32_t x4_over_4_plus_x3_over_6_plus_x2_over_2 = DIV_POW2(SAT_HIGH_MUL(x4_over_4 + x3, one_over_3) + x2, 1);
|
||||
int32_t result = one_over_8 + SAT_HIGH_MUL(one_over_8, x + x4_over_4_plus_x3_over_6_plus_x2_over_2);
|
||||
|
||||
#define SELECT_IF_NON_ZERO(x) { \
|
||||
mask = MASK_IF_NON_ZERO(remainder & (1 << shift++)); \
|
||||
result = SELECT_USING_MASK(mask, SAT_HIGH_MUL(result, x), result); \
|
||||
}
|
||||
|
||||
SELECT_IF_NON_ZERO(1672461947)
|
||||
SELECT_IF_NON_ZERO(1302514674)
|
||||
SELECT_IF_NON_ZERO(790015084)
|
||||
SELECT_IF_NON_ZERO(290630308)
|
||||
SELECT_IF_NON_ZERO(39332535)
|
||||
SELECT_IF_NON_ZERO(720401)
|
||||
SELECT_IF_NON_ZERO(242)
|
||||
|
||||
#undef SELECT_IF_NON_ZERO
|
||||
|
||||
mask = MASK_IF_ZERO(val);
|
||||
return SELECT_USING_MASK(mask, INT32_MAX, result);
|
||||
}
|
||||
9
code/components/esp-nn/test_app/CMakeLists.txt
Normal file
9
code/components/esp-nn/test_app/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../" "../tests/")
|
||||
set(IDF_EXCLUDE_COMPONENTS test test_app)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(test_app)
|
||||
7
code/components/esp-nn/test_app/main/CMakeLists.txt
Normal file
7
code/components/esp-nn/test_app/main/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
set(COMPONENT_SRCS "main.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS "")
|
||||
|
||||
set(COMPONENT_PRIV_REQUIRES tests)
|
||||
|
||||
register_component()
|
||||
8
code/components/esp-nn/test_app/main/component.mk
Normal file
8
code/components/esp-nn/test_app/main/component.mk
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# Main component makefile.
|
||||
#
|
||||
# This Makefile can be left empty. By default, it will take the sources in the
|
||||
# src/ directory, compile them and link them into lib(subdirectory_name).a
|
||||
# in the build directory. This behaviour is entirely configurable,
|
||||
# please read the ESP-IDF documents if you need to do this.
|
||||
#
|
||||
87
code/components/esp-nn/test_app/main/main.c
Normal file
87
code/components/esp-nn/test_app/main/main.c
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <test_functions.h>
|
||||
#include <esp_timer.h>
|
||||
|
||||
static const char *TAG = "test_app";
|
||||
static uint32_t start_c, start_opt, total_c, total_opt;
|
||||
|
||||
void profile_c_start()
|
||||
{
|
||||
/* initiate profiling */
|
||||
start_c = esp_cpu_get_ccount();
|
||||
}
|
||||
|
||||
void profile_c_end()
|
||||
{
|
||||
/* record profile number */
|
||||
total_c = esp_cpu_get_ccount() - start_c;
|
||||
}
|
||||
|
||||
void profile_opt_start()
|
||||
{
|
||||
/* initiate profiling */
|
||||
start_opt = esp_cpu_get_ccount();
|
||||
}
|
||||
|
||||
void profile_opt_end()
|
||||
{
|
||||
/* record profile number */
|
||||
total_opt = esp_cpu_get_ccount() - start_opt;
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
/* s8 tests */
|
||||
ESP_LOGI(TAG, "Running s8 tests...");
|
||||
esp_nn_add_elementwise_s8_test();
|
||||
printf("add, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_mul_elementwise_s8_test();
|
||||
printf("mul, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_depthwise_conv_s8_test();
|
||||
printf("depthwise, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_conv_s8_test();
|
||||
printf("conv2d, c %u opt %u\n", total_c, total_opt);
|
||||
|
||||
esp_nn_relu6_s8_test();
|
||||
printf("relu, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_avg_pool_s8_test();
|
||||
printf("avg_pool, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_max_pool_s8_test();
|
||||
printf("max_pool, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_fully_connected_s8_test();
|
||||
printf("fully_connected, c %u opt %u\n", total_c, total_opt);
|
||||
esp_nn_softmax_s8_test();
|
||||
printf("softmax, c %u opt %u\n", total_c, total_opt);
|
||||
ESP_LOGI(TAG, "s8 tests done!\n");
|
||||
|
||||
/* u8 tests */
|
||||
//ESP_LOGI(TAG, "Running u8 tests...");
|
||||
//esp_nn_add_elementwise_u8_test();
|
||||
//esp_nn_depthwise_conv_u8_test();
|
||||
//esp_nn_conv_u8_test();
|
||||
//esp_nn_avg_pool_u8_test();
|
||||
//esp_nn_max_pool_u8_test();
|
||||
//esp_nn_fully_connected_u8_test();
|
||||
//ESP_LOGI(TAG, "u8 tests done!\n");
|
||||
}
|
||||
5
code/components/esp-nn/test_app/sdkconfig.defaults
Normal file
5
code/components/esp-nn/test_app/sdkconfig.defaults
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
#
|
||||
# esp-nn
|
||||
#
|
||||
CONFIG_NN_ESP32=y
|
||||
@@ -0,0 +1,8 @@
|
||||
# Default configurations for ESP32-S3
|
||||
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
|
||||
|
||||
CONFIG_ESP32S3_DATA_CACHE_64KB=y
|
||||
CONFIG_ESP32S3_DATA_CACHE_8WAYS=y
|
||||
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
|
||||
15
code/components/esp-nn/tests/CMakeLists.txt
Normal file
15
code/components/esp-nn/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ./include/)
|
||||
set(COMPONENT_SRCS "src/basic_math_test.c"
|
||||
"src/convolution_test.c"
|
||||
"src/fully_connected_test.c"
|
||||
"src/pooling_test.c"
|
||||
"src/relu_test.c"
|
||||
"src/softmax_test.c")
|
||||
|
||||
set(COMPONENT_REQUIRES )
|
||||
set(COMPONENT_PRIV_REQUIRES esp-nn)
|
||||
|
||||
register_component()
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-function)
|
||||
4
code/components/esp-nn/tests/README.md
Normal file
4
code/components/esp-nn/tests/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Tests for esp_nn library
|
||||
|
||||
- Include these in your test framework and run the framework.
|
||||
- For IDF test please refer `test_app`
|
||||
5
code/components/esp-nn/tests/component.mk
Normal file
5
code/components/esp-nn/tests/component.mk
Normal file
@@ -0,0 +1,5 @@
|
||||
#FIXME
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := include/
|
||||
|
||||
COMPONENT_SRCDIRS := src/
|
||||
48
code/components/esp-nn/tests/include/test_functions.h
Normal file
48
code/components/esp-nn/tests/include/test_functions.h
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
|
||||
/* int8_t ops tests */
|
||||
void esp_nn_add_elementwise_s8_test();
|
||||
void esp_nn_mul_elementwise_s8_test();
|
||||
|
||||
void esp_nn_depthwise_conv_s8_test();
|
||||
void esp_nn_conv_s8_test();
|
||||
|
||||
void esp_nn_avg_pool_s8_test();
|
||||
void esp_nn_max_pool_s8_test();
|
||||
|
||||
void esp_nn_fully_connected_s8_test();
|
||||
|
||||
void esp_nn_relu6_s8_test();
|
||||
|
||||
void esp_nn_softmax_s8_test();
|
||||
|
||||
/* uint8_t ops tests */
|
||||
void esp_nn_add_elementwise_u8_test();
|
||||
|
||||
void esp_nn_depthwise_conv_u8_test();
|
||||
void esp_nn_conv_u8_test();
|
||||
|
||||
void esp_nn_avg_pool_u8_test();
|
||||
void esp_nn_max_pool_u8_test();
|
||||
|
||||
void esp_nn_fully_connected_u8_test();
|
||||
|
||||
/* instructions test functions */
|
||||
void compare_instructions_test();
|
||||
void arith_instructions_test();
|
||||
void min_max_instructions_test();
|
||||
void bitwise_instructions_test();
|
||||
void load_store_instructions_test();
|
||||
87
code/components/esp-nn/tests/include/test_utils.h
Normal file
87
code/components/esp-nn/tests/include/test_utils.h
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <common_functions.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* mult value range */
|
||||
#define MULT_MAX INT32_MAX
|
||||
#define MULT_MIN 0
|
||||
|
||||
/* shift value range */
|
||||
#define SHIFT_MIN -31
|
||||
#define SHIFT_MAX 30
|
||||
|
||||
/**
|
||||
* @brief callback function to run before C function
|
||||
*/
|
||||
void profile_c_start();
|
||||
|
||||
/**
|
||||
* @brief callback function to run after C function
|
||||
*/
|
||||
void profile_c_end();
|
||||
|
||||
/**
|
||||
* @brief callback function to run before optimized function
|
||||
*/
|
||||
void profile_opt_start();
|
||||
|
||||
/**
|
||||
* @brief callback function to run after optimized function
|
||||
*/
|
||||
void profile_opt_end();
|
||||
|
||||
#define ANSI_COLOR_RED "\x1b[31m"
|
||||
#define ANSI_COLOR_GREEN "\x1b[32m"
|
||||
#define ANSI_COLOR_YELLOW "\x1b[33m"
|
||||
#define ANSI_COLOR_BLUE "\x1b[34m"
|
||||
#define ANSI_COLOR_MAGENTA "\x1b[35m"
|
||||
#define ANSI_COLOR_CYAN "\x1b[36m"
|
||||
#define ANSI_COLOR_RESET "\x1b[0m"
|
||||
|
||||
#define CHECK_EQUAL(ARRAY1, ARRAY2, size) ({ \
|
||||
bool res = true; \
|
||||
for (int _i = 0; _i < size; _i++) { \
|
||||
if (ARRAY1[_i] != ARRAY2[_i]) { \
|
||||
res = false; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
res; \
|
||||
})
|
||||
|
||||
#define PRINT_ARRAY_INT(ARRAY, width, height) ({ \
|
||||
int *_array = (int *) ARRAY; \
|
||||
for (int _j = 0; _j < height; _j++) { \
|
||||
for (int _i = 0; _i < width; _i++) { \
|
||||
printf("%d\t", _array[width * _j + _i]); \
|
||||
} \
|
||||
printf("\n"); \
|
||||
} \
|
||||
printf("\n"); \
|
||||
})
|
||||
|
||||
#define PRINT_ARRAY_HEX(ARRAY, width, height) ({ \
|
||||
uint8_t *_array = (uint8_t *) ARRAY; \
|
||||
for (int _j = 0; _j < height; _j++) { \
|
||||
for (int _i = 0; _i < width; _i++) { \
|
||||
printf("%02x\t", _array[width * _j + _i]); \
|
||||
} \
|
||||
printf("\n"); \
|
||||
} \
|
||||
printf("\n"); \
|
||||
})
|
||||
355
code/components/esp-nn/tests/src/basic_math_test.c
Normal file
355
code/components/esp-nn/tests/src/basic_math_test.c
Normal file
@@ -0,0 +1,355 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <common_functions.h>
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
#if CONFIG_IDF_CMAKE
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
#define IDF_HEAP_CAPS 1
|
||||
#endif
|
||||
|
||||
#if IDF_HEAP_CAPS
|
||||
#include "esp_heap_caps.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void esp_nn_add_elementwise_s8_test()
|
||||
{
|
||||
/* prepare data */
|
||||
const int size = 1600 + 8 + 7; /* odd len to test leftover */
|
||||
int8_t *input1;
|
||||
int8_t *input2;
|
||||
int8_t *out_data_c;
|
||||
int8_t *out_data_opt;
|
||||
int8_t *input1_orig = NULL;
|
||||
int8_t *input2_orig = NULL;
|
||||
int8_t *out_c_orig = NULL;
|
||||
int8_t *out_opt_orig = NULL;
|
||||
int32_t input1_offset = 34;
|
||||
int32_t input2_offset = 35;
|
||||
int32_t output_offset = 36;
|
||||
int32_t input1_shift = -8; // right_shift amt always <= 0
|
||||
int32_t input2_shift = -8; // right_shift amt always <= 0
|
||||
int32_t output_shift = -9; // right_shift amt always <= 0
|
||||
int32_t left_shift = 15; // always +ve
|
||||
int32_t input1_mult = INT32_MAX;
|
||||
int32_t input2_mult = INT32_MAX;
|
||||
int32_t output_mult = INT32_MAX;
|
||||
int32_t activation_min = -128;
|
||||
int32_t activation_max = 127;
|
||||
|
||||
for (int itr = 0; itr < 10; itr++) {
|
||||
switch (itr) {
|
||||
case 0: // all zeros
|
||||
input1_offset = 0;
|
||||
input2_offset = 0;
|
||||
output_offset = 0;
|
||||
input1_mult = 0;
|
||||
input2_mult = 0;
|
||||
output_mult = 0;
|
||||
input1_shift = 0;
|
||||
input2_shift = 0;
|
||||
output_shift = 0;
|
||||
left_shift = 0;
|
||||
break;
|
||||
case 1: // hit min
|
||||
input1_offset = -127;
|
||||
input2_offset = -127;
|
||||
output_offset = -128;
|
||||
input1_mult = MULT_MIN;
|
||||
input2_mult = MULT_MIN;
|
||||
output_mult = MULT_MIN;
|
||||
input1_shift = 0;
|
||||
input2_shift = 0;
|
||||
output_shift = 0;
|
||||
left_shift = 0;
|
||||
break;
|
||||
case 2: // hit max
|
||||
input1_offset = 128;
|
||||
input2_offset = 128;
|
||||
output_offset = -127;
|
||||
input1_mult = MULT_MAX;
|
||||
input2_mult = MULT_MAX;
|
||||
output_mult = MULT_MAX;
|
||||
input1_shift = SHIFT_MIN;
|
||||
input2_shift = SHIFT_MIN;
|
||||
output_shift = SHIFT_MIN;
|
||||
left_shift = 30 - 8; // since input is 8 bits
|
||||
break;
|
||||
case 3: // hit extreme max
|
||||
input1_offset = 128;
|
||||
input2_offset = 128;
|
||||
output_offset = -127;
|
||||
input1_mult = MULT_MAX;
|
||||
input2_mult = MULT_MAX;
|
||||
output_mult = MULT_MAX;
|
||||
input1_shift = 0;
|
||||
input2_shift = 0;
|
||||
output_shift = 0;
|
||||
left_shift = 30 - 8; // -8 since input is 8 bit
|
||||
break;
|
||||
default: // practical random input
|
||||
input1_offset = rand() % 256 - 127; // range [-127, 128]
|
||||
input2_offset = rand() % 256 - 127; // range [-127, 128]
|
||||
output_offset = rand() % 256 - 128; // range [-128, 127]
|
||||
input1_mult = MULT_MAX / 2 + rand() % INT16_MAX;
|
||||
input2_mult = MULT_MAX / 2 + rand() % INT16_MAX;
|
||||
output_mult = MULT_MAX / 2 + rand() % INT16_MAX;
|
||||
input1_shift = -8 + rand() % 4;
|
||||
input2_shift = -8 + rand() % 4;
|
||||
output_shift = -8 + rand() % 4;
|
||||
left_shift = rand() % 15;
|
||||
}
|
||||
#if IDF_HEAP_CAPS
|
||||
input1_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
input2_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_c_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_opt_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
|
||||
input1 = 16 + input1_orig - ((uint32_t) input1_orig & 0xf);
|
||||
input2 = 16 + input2_orig - ((uint32_t) input2_orig & 0xf);
|
||||
out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
|
||||
out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
|
||||
#else
|
||||
input1 = memalign(16, size);
|
||||
input2 = memalign(16, size);
|
||||
out_data_c = memalign(16, size);
|
||||
out_data_opt = memalign(16, size);
|
||||
|
||||
input1_orig = input1;
|
||||
input2_orig = input2;
|
||||
out_c_orig = out_data_c;
|
||||
out_opt_orig = out_data_opt;
|
||||
#endif
|
||||
if (input1_orig == NULL || input2_orig == NULL || out_c_orig == NULL ||
|
||||
out_opt_orig == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s error allocating buffers\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto elementwise_add_test_cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input1[i] = rand() % 256 - 128;
|
||||
input2[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
if (itr == 0) {
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
}
|
||||
/* C function */
|
||||
esp_nn_add_elementwise_s8_ansi(input1, input2, input1_offset, input2_offset,
|
||||
input1_mult, input2_mult, input1_shift, input2_shift,
|
||||
left_shift, out_data_c, output_offset, output_mult,
|
||||
output_shift, activation_min, activation_max, size);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
}
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_add_elementwise_s8(input1, input2, input1_offset, input2_offset,
|
||||
input1_mult, input2_mult, input1_shift, input2_shift,
|
||||
left_shift, out_data_opt, output_offset, output_mult,
|
||||
output_shift, activation_min, activation_max, size);
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
}
|
||||
|
||||
bool ret = CHECK_EQUAL(out_data_c, out_data_opt, size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(out_data_opt, size, 1);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(out_data_c, size, 1);
|
||||
printf("Input1:\n");
|
||||
PRINT_ARRAY_HEX(input1, size, 1);
|
||||
printf("Input2:\n");
|
||||
PRINT_ARRAY_HEX(input2, size, 1);
|
||||
printf("in1_shift %d, in2_shift %d, left_shift %d, out_shift %d\n",
|
||||
input1_shift, input2_shift, left_shift, output_shift);
|
||||
printf("in1_mult %d, in2_mult %d, out_mult %d\n", input1_mult, input2_mult, output_mult);
|
||||
goto elementwise_add_test_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
|
||||
elementwise_add_test_cleanup:
|
||||
if (input1_orig) {
|
||||
free(input1_orig);
|
||||
}
|
||||
if (input2_orig) {
|
||||
free(input2_orig);
|
||||
}
|
||||
if (out_c_orig) {
|
||||
free(out_c_orig);
|
||||
}
|
||||
if (out_opt_orig) {
|
||||
free(out_opt_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_mul_elementwise_s8_test()
|
||||
{
|
||||
/* prepare data */
|
||||
const int size = 1600 + 8 + 7; /* odd len to test leftover */
|
||||
int8_t *input1;
|
||||
int8_t *input2;
|
||||
int8_t *out_data_c;
|
||||
int8_t *out_data_opt;
|
||||
int32_t input1_offset = 34;
|
||||
int32_t input2_offset = 35;
|
||||
int32_t output_offset = 36;
|
||||
int32_t output_shift = -7;
|
||||
int32_t output_mult = MULT_MAX; // max out_mult
|
||||
int32_t activation_min = -128;
|
||||
int32_t activation_max = 127;
|
||||
int8_t *input1_orig = NULL;
|
||||
int8_t *input2_orig = NULL;
|
||||
int8_t *out_c_orig = NULL;
|
||||
int8_t *out_opt_orig = NULL;
|
||||
|
||||
for (int itr = 0; itr < 10; itr++) {
|
||||
switch (itr) {
|
||||
case 0: // all zeros
|
||||
input1_offset = 0;
|
||||
input2_offset = 0;
|
||||
output_offset = 0;
|
||||
output_mult = 0;
|
||||
output_shift = 0;
|
||||
break;
|
||||
case 1: // hit min
|
||||
input1_offset = -127;
|
||||
input2_offset = -127;
|
||||
output_offset = -128;
|
||||
output_mult = MULT_MIN;
|
||||
output_shift = 0;
|
||||
break;
|
||||
case 2: // hit max
|
||||
input1_offset = 128;
|
||||
input2_offset = 128;
|
||||
output_offset = -127;
|
||||
output_mult = MULT_MAX;
|
||||
output_shift = SHIFT_MIN;
|
||||
break;
|
||||
case 3: // hit extreme max
|
||||
input1_offset = 128;
|
||||
input2_offset = 128;
|
||||
output_offset = -127;
|
||||
output_mult = MULT_MAX;
|
||||
output_shift = 0;
|
||||
break;
|
||||
default: // practical random input
|
||||
input1_offset = rand() % 256 - 127; // range [-127, 128]
|
||||
input2_offset = rand() % 256 - 127; // range [-127, 128]
|
||||
output_offset = rand() % 256 - 128; // range [-128, 127]
|
||||
output_mult = MULT_MAX / 2 + rand() % INT16_MAX;
|
||||
output_shift = -8 + rand() % 4;
|
||||
}
|
||||
|
||||
#if IDF_HEAP_CAPS
|
||||
input1_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
input2_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_c_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_opt_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
|
||||
input1 = 16 + input1_orig - ((uint32_t) input1_orig & 0xf);
|
||||
input2 = 16 + input2_orig - ((uint32_t) input2_orig & 0xf);
|
||||
out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
|
||||
out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
|
||||
#else
|
||||
input1 = memalign(16, size);
|
||||
input2 = memalign(16, size);
|
||||
out_data_c = memalign(16, size);
|
||||
out_data_opt = memalign(16, size);
|
||||
|
||||
input1_orig = input1;
|
||||
input2_orig = input2;
|
||||
out_c_orig = out_data_c;
|
||||
out_opt_orig = out_data_opt;
|
||||
#endif
|
||||
if (input1_orig == NULL || input2_orig == NULL || out_c_orig == NULL ||
|
||||
out_opt_orig == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s error allocating buffers\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto elementwise_mult_test_cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input1[i] = rand() % 256 - 128;
|
||||
input2[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
if (itr == 0) {
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
}
|
||||
/* C function */
|
||||
esp_nn_mul_elementwise_s8_ansi(input1, input2, input1_offset, input2_offset,
|
||||
out_data_c, output_offset, output_mult, output_shift,
|
||||
activation_min, activation_max, size);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
}
|
||||
/* Optimized function */
|
||||
esp_nn_mul_elementwise_s8(input1, input2, input1_offset, input2_offset,
|
||||
out_data_opt, output_offset, output_mult, output_shift,
|
||||
activation_min, activation_max, size);
|
||||
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
}
|
||||
|
||||
bool ret = CHECK_EQUAL(out_data_c, out_data_opt, size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(out_data_opt, size, 1);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(out_data_c, size, 1);
|
||||
printf("Input1:\n");
|
||||
PRINT_ARRAY_HEX(input1, size, 1);
|
||||
printf("Input2:\n");
|
||||
PRINT_ARRAY_HEX(input2, size, 1);
|
||||
goto elementwise_mult_test_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
|
||||
elementwise_mult_test_cleanup:
|
||||
if (input1_orig) {
|
||||
free(input1_orig);
|
||||
}
|
||||
if (input2_orig) {
|
||||
free(input2_orig);
|
||||
}
|
||||
if (out_c_orig) {
|
||||
free(out_c_orig);
|
||||
}
|
||||
if (out_opt_orig) {
|
||||
free(out_opt_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
605
code/components/esp-nn/tests/src/convolution_test.c
Normal file
605
code/components/esp-nn/tests/src/convolution_test.c
Normal file
@@ -0,0 +1,605 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
#if CONFIG_IDF_CMAKE
|
||||
#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
|
||||
#define IDF_HEAP_CAPS 1
|
||||
#endif
|
||||
#if IDF_HEAP_CAPS
|
||||
#include "esp_heap_caps.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void esp_nn_depthwise_conv_s8_test()
|
||||
{
|
||||
int8_t *input = NULL, *filter_data = NULL, *out_data_c = NULL, *out_data_opt = NULL;
|
||||
int32_t *bias = NULL;
|
||||
int32_t input_offset = 5; /* some number in [-128, 127] */
|
||||
int32_t out_offset = 7;
|
||||
int32_t activation_min = -125;
|
||||
int32_t activation_max = 120;
|
||||
void *scratch_buf = NULL;
|
||||
|
||||
/* independent variables */
|
||||
int input_wd, input_ht, channels;
|
||||
uint16_t filter_ht, filter_wd, ch_mult;
|
||||
uint16_t pad_wd, pad_ht, stride_wd, stride_ht;
|
||||
|
||||
// run for 15 iterations
|
||||
for (int itr = 0; itr < 15; itr++) {
|
||||
/* prepare data */
|
||||
switch (itr) {
|
||||
case 0: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (0,0)
|
||||
input_wd = 18;
|
||||
input_ht = 18;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 1: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (1,1)
|
||||
input_wd = 10;
|
||||
input_ht = 10;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 2: // (ch_mult 1, (channels % 8) = 0), filter (3,3), pad (1,1)
|
||||
input_wd = 10;
|
||||
input_ht = 10;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 24;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 3: // other filter sizes (ch_mult 1, (channels % 8) = 0)
|
||||
input_wd = 10;
|
||||
input_ht = 10;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 24;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 4: // other filter sizes (ch_mult 8 = 0)
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 8;
|
||||
channels = 4;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 5: // other filter sizes (ch_mult 8 = 0)
|
||||
input_wd = 12;
|
||||
input_ht = 12;
|
||||
filter_ht = 5;
|
||||
filter_wd = 5;
|
||||
ch_mult = 8;
|
||||
channels = 4;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 6: // other filter sizes (ch_mult 4 = 0)
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 4;
|
||||
channels = 4;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 7: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (0,0) stride (2,2)
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 2;
|
||||
stride_ht = 2;
|
||||
break;
|
||||
case 8: // same as case 7, with large parameters
|
||||
input_wd = 58;
|
||||
input_ht = 58;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 128;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 2;
|
||||
stride_ht = 2;
|
||||
break;
|
||||
case 9: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (0,0) stride (2,2)
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 2;
|
||||
stride_ht = 2;
|
||||
break;
|
||||
default:
|
||||
input_wd = 6;
|
||||
input_ht = 6;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
ch_mult = 1;
|
||||
channels = 16;
|
||||
stride_wd = rand() % 2 + 1;
|
||||
stride_ht = stride_wd;
|
||||
pad_wd = stride_wd == 1 ? 0 : rand() % 2;
|
||||
pad_ht = pad_wd;
|
||||
printf("stride(%d), pad (%d)\t", stride_wd, pad_wd);
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t out_wd = (input_wd - filter_wd + 1) / stride_wd;
|
||||
uint16_t out_ht = (input_ht - filter_ht + 1) / stride_ht;
|
||||
if (itr == 9) {
|
||||
// expect the function to handle this gracefully
|
||||
out_wd += 1;
|
||||
out_ht += 1;
|
||||
}
|
||||
int in_size = input_wd * input_ht * channels;
|
||||
int out_size = out_wd * out_ht * channels * ch_mult;
|
||||
int filter_size = filter_wd * filter_ht * channels * ch_mult + 4;
|
||||
int bias_size = channels * ch_mult + 1;
|
||||
int32_t out_shift[channels * ch_mult];
|
||||
int32_t out_mult[channels * ch_mult];
|
||||
|
||||
#if IDF_HEAP_CAPS
|
||||
int8_t *input_orig = (int8_t *) heap_caps_malloc(in_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
int8_t *out_c_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
int8_t *out_opt_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
filter_data = (int8_t *) heap_caps_malloc(filter_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
bias = (int32_t *) heap_caps_malloc(bias_size * 4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
|
||||
input = 16 + input_orig - ((uint32_t) input_orig & 0xf);
|
||||
out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
|
||||
out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
|
||||
#else
|
||||
input = memalign(16, in_size + 16);
|
||||
filter_data = memalign(16, filter_size);
|
||||
out_data_c = memalign(16, out_size + 16);
|
||||
out_data_opt = memalign(16, out_size + 16);
|
||||
bias = memalign(16, bias_size * 4);
|
||||
int8_t *input_orig = input;
|
||||
int8_t *out_c_orig = out_data_c;
|
||||
int8_t *out_opt_orig = out_data_opt;
|
||||
#endif
|
||||
if (bias == NULL || input == NULL || filter_data == NULL ||
|
||||
out_data_c == NULL || out_data_opt == NULL || bias == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
goto dc_s8_cleanup;
|
||||
}
|
||||
|
||||
/* Generate input data */
|
||||
for (int i = 0; i < in_size; ++i) {
|
||||
input[i] = rand() % 128;
|
||||
}
|
||||
|
||||
/* Generate filter data */
|
||||
for (int i = 0; i < filter_size; ++i) {
|
||||
filter_data[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
/* Generate bias data */
|
||||
for (int i = 0; i < channels * ch_mult; ++i) {
|
||||
bias[i + 1] = rand() % INT16_MAX; //0th index left for unalignment
|
||||
out_shift[i] = -8 + rand() % 3;
|
||||
out_mult[i] = 0x7eb0e200 + rand() % 50;
|
||||
}
|
||||
|
||||
data_dims_t input_dims = {.width = input_wd, .height = input_ht, .channels = channels, 1};
|
||||
data_dims_t output_dims = {.width = out_wd, .height = out_ht, .channels = channels * ch_mult, 1};
|
||||
data_dims_t filter_dims = {.width = filter_wd, .height = filter_ht, 0, 0};
|
||||
dw_conv_params_t conv_params = {.in_offset = input_offset, .out_offset = out_offset, .ch_mult = ch_mult,
|
||||
.stride = {stride_wd, stride_ht}, .padding = {pad_wd, pad_ht},
|
||||
.dilation = {0, 0}, .activation = {activation_min, activation_max}};
|
||||
quant_data_t quant_data = {.shift = out_shift, .mult = out_mult};
|
||||
|
||||
int scratch_buf_size = esp_nn_get_depthwise_conv_scratch_size(&input_dims, &filter_dims,
|
||||
&output_dims, &conv_params);
|
||||
if (scratch_buf_size > 0) {
|
||||
#if IDF_HEAP_CAPS
|
||||
scratch_buf = heap_caps_malloc(scratch_buf_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
int align_sz = 16 - (((int32_t) scratch_buf) & 0xf);
|
||||
#else
|
||||
scratch_buf = memalign(16, scratch_buf_size);
|
||||
int align_sz = 0;
|
||||
#endif
|
||||
if (scratch_buf == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] scratch_buf alloc failed size %d\n"ANSI_COLOR_RESET,
|
||||
__FUNCTION__, itr, scratch_buf_size);
|
||||
goto dc_s8_cleanup;
|
||||
}
|
||||
esp_nn_set_depthwise_conv_scratch_buf(scratch_buf + align_sz);
|
||||
}
|
||||
if (itr == 0) {
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
}
|
||||
|
||||
/* C function */
|
||||
esp_nn_depthwise_conv_s8_ansi(&input_dims, input, &filter_dims, filter_data + 4,
|
||||
bias + 1, &output_dims, out_data_c, &conv_params, &quant_data);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
}
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_depthwise_conv_s8(&input_dims, input, &filter_dims, filter_data + 4,
|
||||
bias + 1, &output_dims, out_data_opt, &conv_params, &quant_data);
|
||||
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
}
|
||||
|
||||
bool ret = CHECK_EQUAL(out_data_c, out_data_opt, out_size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(out_data_opt, out_size / out_ht, out_ht);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(out_data_c, out_size / out_ht, out_ht);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, in_size / input_ht, input_ht);
|
||||
printf("Filter data:\n");
|
||||
PRINT_ARRAY_HEX(filter_data + 4, (filter_size - 4) / filter_ht, filter_ht);
|
||||
printf("bias data:\n");
|
||||
PRINT_ARRAY_INT(bias + 1, ch_mult * channels, 1);
|
||||
goto dc_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
|
||||
dc_s8_cleanup:
|
||||
if (input) {
|
||||
free(input_orig);
|
||||
}
|
||||
if (filter_data) {
|
||||
free(filter_data);
|
||||
}
|
||||
if (out_data_c) {
|
||||
free(out_c_orig);
|
||||
}
|
||||
if (out_data_opt) {
|
||||
free(out_opt_orig);
|
||||
}
|
||||
if (bias) {
|
||||
free(bias);
|
||||
}
|
||||
if (scratch_buf) {
|
||||
free(scratch_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_conv_s8_test()
|
||||
{
|
||||
const int32_t input_offset = 5; /* some number in [-128, 127] */
|
||||
const int32_t activation_min = -125;
|
||||
const int32_t activation_max = 122;
|
||||
const int32_t out_offset = 3;
|
||||
|
||||
void *scratch_buf = NULL;
|
||||
int8_t *input_orig;
|
||||
int8_t *out_c_orig;
|
||||
int8_t *out_opt_orig;
|
||||
int8_t *filter_data;
|
||||
int32_t *bias;
|
||||
|
||||
/* independent variable */
|
||||
int in_wd, in_ht, in_channels, out_channels;
|
||||
uint16_t filter_ht, filter_wd;
|
||||
uint16_t pad_wd, pad_ht, stride_wd, stride_ht;
|
||||
|
||||
// run for 10 iterations
|
||||
for (int itr = 0; itr < 10; itr++) {
|
||||
switch (itr) {
|
||||
case 0: // ch % 8 == 0 && filter (1,1), padding (0,0)
|
||||
in_wd = 10;
|
||||
in_ht = 10;
|
||||
in_channels = 64;
|
||||
out_channels = 64;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 1: // ch % 4 == 0 && (in_wd * in_ht) % 16 == 0
|
||||
in_wd = 4;
|
||||
in_ht = 4;
|
||||
in_channels = 20;
|
||||
out_channels = 8;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 2: // ch, filter (3x3x3)
|
||||
in_wd = 10;
|
||||
in_ht = 10;
|
||||
in_channels = 3;
|
||||
out_channels = 64;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 3: // remaining pad (0, 0)
|
||||
in_wd = 10;
|
||||
in_ht = 10;
|
||||
in_channels = 3;
|
||||
out_channels = 64;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 4: // unopt case
|
||||
in_wd = 10;
|
||||
in_ht = 10;
|
||||
in_channels = 12;
|
||||
out_channels = 64;
|
||||
filter_ht = 3;
|
||||
filter_wd = 3;
|
||||
pad_wd = 1;
|
||||
pad_ht = 1;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
case 5: // ch % 8 == 0 & stride (2,2)
|
||||
in_wd = 16;
|
||||
in_ht = 16;
|
||||
in_channels = 16;
|
||||
out_channels = 16;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 2;
|
||||
stride_ht = 2;
|
||||
break;
|
||||
case 6: // ch % 8 == 0 && filter (1,1), padding (0,0)
|
||||
in_wd = 2;
|
||||
in_ht = 2;
|
||||
in_channels = 8;
|
||||
out_channels = 8;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
default: // ch % 8 == 0
|
||||
in_wd = 8;
|
||||
in_ht = 8;
|
||||
in_channels = 16;
|
||||
out_channels = 16;
|
||||
filter_ht = 1;
|
||||
filter_wd = 1;
|
||||
pad_wd = 0;
|
||||
pad_ht = 0;
|
||||
stride_wd = 1;
|
||||
stride_ht = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* prepare data */
|
||||
uint16_t out_wd = (in_wd - filter_wd + 1) / stride_wd;
|
||||
uint16_t out_ht = (in_ht - filter_ht + 1) / stride_ht;
|
||||
|
||||
int in_size = in_wd * in_ht * in_channels;
|
||||
int filter_size = filter_wd * filter_ht * in_channels * out_channels + 2;
|
||||
int out_size = out_wd * out_ht * out_channels;
|
||||
|
||||
#if IDF_HEAP_CAPS
|
||||
input_orig = (int8_t *) heap_caps_malloc(in_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_c_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
out_opt_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
filter_data = (int8_t *) heap_caps_malloc(filter_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
bias = (int32_t *) heap_caps_malloc(128 + sizeof (int32_t) * out_channels, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
|
||||
int8_t *input = 16 + input_orig - ((uint32_t) input_orig & 0xf);
|
||||
int8_t *out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
|
||||
int8_t *out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
|
||||
#else
|
||||
int8_t *input = memalign(16, in_size);
|
||||
int8_t *out_data_c = memalign(16, out_size);
|
||||
int8_t *out_data_opt = memalign(16, out_size);
|
||||
filter_data = memalign(16, filter_size);
|
||||
bias = calloc(1, 128 + sizeof (int32_t) * out_channels);
|
||||
input_orig = input;
|
||||
out_c_orig = out_data_c;
|
||||
out_opt_orig = out_data_opt;
|
||||
#endif
|
||||
int32_t *out_shift = calloc(1, 128 + sizeof (int32_t) * out_channels);
|
||||
int32_t *out_mult = calloc(1, 128 + sizeof (int32_t) * out_channels);
|
||||
|
||||
if (input == NULL || filter_data == NULL ||
|
||||
out_data_c == NULL || out_data_opt == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto conv_s8_cleanup;
|
||||
}
|
||||
|
||||
if (bias == NULL || out_shift == NULL || out_mult == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto conv_s8_cleanup;
|
||||
}
|
||||
|
||||
/* Generate input data between -128 -> +127 */
|
||||
for (int i = 0; i < in_size; ++i) {
|
||||
input[i] = rand() % 255 - 128;
|
||||
}
|
||||
|
||||
/* Generate filter data between -128 -> +127 */
|
||||
for (int i = 0; i < filter_size; ++i) {
|
||||
filter_data[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
/* Generate bias data */
|
||||
for (int i = 0; i < out_channels; ++i) {
|
||||
bias[i] = (int32_t)rand() % UINT16_MAX + UINT8_MAX;
|
||||
}
|
||||
|
||||
/* Shift and multiplier */
|
||||
for (int i = 0; i < out_channels; ++i) {
|
||||
out_shift[i] = -10 + rand() % 2;
|
||||
out_mult[i] = 0x7f67f4f8 + rand() % 50;
|
||||
}
|
||||
|
||||
data_dims_t input_dims = {.width = in_wd, .height = in_ht, .channels = in_channels, 1};
|
||||
data_dims_t output_dims = {.width = out_wd, .height = out_ht, .channels = out_channels, 1};
|
||||
data_dims_t filter_dims = {.width = filter_wd, .height = filter_ht, 0, 0};
|
||||
conv_params_t conv_params = {.in_offset = input_offset, .out_offset = out_offset,
|
||||
.stride = {stride_wd, stride_ht}, .padding = {pad_wd, pad_ht},
|
||||
.dilation = {0, 0}, .activation = {activation_min, activation_max}};
|
||||
quant_data_t quant_data = {.shift = out_shift, .mult = out_mult};
|
||||
|
||||
int scratch_buf_size = esp_nn_get_conv_scratch_size(&input_dims, &filter_dims,
|
||||
&output_dims, &conv_params);
|
||||
if (scratch_buf_size > 0) {
|
||||
#if IDF_HEAP_CAPS
|
||||
void *scratch_buf = heap_caps_malloc(scratch_buf_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
int align_sz = 16 - (((int32_t) scratch_buf) & 0xf);
|
||||
#else
|
||||
void *scratch_buf = memalign(16, scratch_buf_size);
|
||||
int align_sz = 0;
|
||||
#endif
|
||||
if (scratch_buf == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s scratch_buf alloc failed size %d\n"ANSI_COLOR_RESET, __FUNCTION__, scratch_buf_size);
|
||||
goto conv_s8_cleanup;
|
||||
}
|
||||
esp_nn_set_conv_scratch_buf(scratch_buf + align_sz);
|
||||
}
|
||||
|
||||
if (itr == 0) {
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
}
|
||||
|
||||
/* C function */
|
||||
esp_nn_conv_s8_ansi(&input_dims, input, &filter_dims, filter_data + 2,
|
||||
bias, &output_dims, out_data_c, &conv_params, &quant_data);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
}
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_conv_s8(&input_dims, input, &filter_dims, filter_data + 2,
|
||||
bias, &output_dims, out_data_opt, &conv_params, &quant_data);
|
||||
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
}
|
||||
|
||||
bool ret = CHECK_EQUAL(out_data_c, out_data_opt, out_size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(out_data_opt, out_size / out_ht, out_ht);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(out_data_c, out_size / out_ht, out_ht);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, in_size / in_ht, in_ht);
|
||||
printf("Filter data:\n");
|
||||
PRINT_ARRAY_HEX(filter_data + 2, (filter_size - 2) / filter_ht, filter_ht);
|
||||
printf("bias data:\n");
|
||||
PRINT_ARRAY_INT(bias, out_channels, 1);
|
||||
goto conv_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
|
||||
conv_s8_cleanup:
|
||||
if (input) {
|
||||
free(input_orig);
|
||||
}
|
||||
if (filter_data) {
|
||||
free(filter_data);
|
||||
}
|
||||
if (out_data_c) {
|
||||
free(out_c_orig);
|
||||
}
|
||||
if (out_data_opt) {
|
||||
free(out_opt_orig);
|
||||
}
|
||||
if (bias) {
|
||||
free(bias);
|
||||
}
|
||||
if (out_shift) {
|
||||
free(out_shift);
|
||||
}
|
||||
if (out_mult) {
|
||||
free(out_mult);
|
||||
}
|
||||
if (scratch_buf) {
|
||||
free(scratch_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
111
code/components/esp-nn/tests/src/fully_connected_test.c
Normal file
111
code/components/esp-nn/tests/src/fully_connected_test.c
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
|
||||
void esp_nn_fully_connected_s8_test()
|
||||
{
|
||||
/* prepare data */
|
||||
static uint16_t row_len = 256 + 8 + 7; /* odd len to test unaligned+left-over */
|
||||
static uint16_t out_channels = 3;
|
||||
int8_t input[row_len];
|
||||
int8_t filter_data[row_len * out_channels];
|
||||
int8_t output_c[out_channels], output_opt[out_channels];
|
||||
static int32_t activation_min = -128;
|
||||
static int32_t activation_max = 127;
|
||||
static int32_t input_offset = 0;
|
||||
static int32_t filter_offset = 0;
|
||||
int32_t out_shift = -10;
|
||||
static int32_t out_offset = 127;
|
||||
int32_t out_mult = 0x59e492c4;
|
||||
for (int itr = 0; itr < 5; itr++) {
|
||||
out_mult = INT32_MAX / row_len + rand() % INT16_MAX;
|
||||
switch (itr) {
|
||||
case 0:
|
||||
out_shift = -10;
|
||||
break;
|
||||
case 1:
|
||||
out_shift = SHIFT_MIN;
|
||||
break;
|
||||
case 2:
|
||||
out_shift = SHIFT_MAX;
|
||||
break;
|
||||
case 3:
|
||||
out_shift = 0;
|
||||
break;
|
||||
default:
|
||||
out_shift = -10 + rand() % 5;
|
||||
break;
|
||||
}
|
||||
if (itr == 0) {
|
||||
out_shift = SHIFT_MAX;
|
||||
}
|
||||
/* Generate input and filter data */
|
||||
for (int i = 0; i < row_len; ++i) {
|
||||
input[i] = rand() % 256 - 128;
|
||||
}
|
||||
for (int i = 0; i < row_len * out_channels; ++i) {
|
||||
filter_data[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
if (itr == 0) {
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
}
|
||||
|
||||
/* C function */
|
||||
esp_nn_fully_connected_s8_ansi(input, input_offset, row_len, filter_data, filter_offset,
|
||||
NULL, output_c, out_channels, out_offset, out_shift, out_mult,
|
||||
activation_min, activation_max);
|
||||
|
||||
if (itr == 0) {
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
}
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_fully_connected_s8(input, input_offset, row_len, filter_data, filter_offset,
|
||||
NULL, output_opt, out_channels, out_offset, out_shift, out_mult,
|
||||
activation_min, activation_max);
|
||||
|
||||
if (itr == 0) {
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
}
|
||||
|
||||
bool ret = CHECK_EQUAL(output_c, output_opt, out_channels);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(output_opt, out_channels, 1);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(output_c, out_channels, 1);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, row_len, 1);
|
||||
printf("Filter data:\n");
|
||||
PRINT_ARRAY_HEX(filter_data, row_len, out_channels);
|
||||
printf("Out shift: %d\n", out_shift);
|
||||
printf("Out mult: %x\n", out_mult);
|
||||
return;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
|
||||
}
|
||||
}
|
||||
184
code/components/esp-nn/tests/src/pooling_test.c
Normal file
184
code/components/esp-nn/tests/src/pooling_test.c
Normal file
@@ -0,0 +1,184 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
|
||||
void esp_nn_avg_pool_s8_test()
|
||||
{
|
||||
/* prepare data */
|
||||
const uint16_t input_wd = 16;
|
||||
const uint16_t input_ht = 16;
|
||||
const uint16_t channels = 16; /* With TFLite example, I have seen it 256 */
|
||||
const int size = input_wd * input_ht * channels;
|
||||
int8_t *input, *output_c, *output_opt;
|
||||
const int32_t activation_min = -128;
|
||||
const int32_t activation_max = 127;
|
||||
const uint16_t pad_wd = 1;
|
||||
const uint16_t pad_ht = 1;
|
||||
const uint16_t stride_wd = 1;
|
||||
const uint16_t stride_ht = 1;
|
||||
const uint16_t filter_ht = 3;
|
||||
const uint16_t filter_wd = 3;
|
||||
const uint16_t out_wd = input_wd / stride_wd;
|
||||
const uint16_t out_ht = input_ht / stride_ht;
|
||||
const int out_size = out_wd * out_ht * channels;
|
||||
|
||||
input = memalign(16, size);
|
||||
output_c = memalign(16, out_size);
|
||||
output_opt = memalign(16, out_size);
|
||||
|
||||
if (input == NULL || output_c == NULL || output_opt == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto avg_pool_s8_cleanup;
|
||||
}
|
||||
/**
|
||||
* width/height, channels etc look suspicious but it it true.
|
||||
* It actually depends upon where in model this is actually placed.
|
||||
* If at the end wd/ht tends to be smaller and depth larger.
|
||||
*/
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
|
||||
/* C function */
|
||||
esp_nn_avg_pool_s8_ansi(input, input_wd, input_ht, output_c, out_wd, out_ht,
|
||||
stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
|
||||
activation_min, activation_max, channels);
|
||||
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_avg_pool_s8(input, input_wd, input_ht, output_opt, out_wd, out_ht,
|
||||
stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
|
||||
activation_min, activation_max, channels);
|
||||
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
|
||||
|
||||
bool ret = CHECK_EQUAL(output_c, output_opt, out_size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(output_opt, out_wd * channels, out_ht);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(output_c, out_wd * channels, out_ht);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, input_wd * channels, input_ht);
|
||||
goto avg_pool_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
|
||||
avg_pool_s8_cleanup:
|
||||
if (input) {
|
||||
free(input);
|
||||
}
|
||||
if (output_c) {
|
||||
free(output_c);
|
||||
}
|
||||
if (output_opt) {
|
||||
free(output_opt);
|
||||
}
|
||||
}
|
||||
|
||||
void esp_nn_max_pool_s8_test()
|
||||
{
|
||||
/* prepare data */
|
||||
const uint16_t input_wd = 16;
|
||||
const uint16_t input_ht = 16;
|
||||
const uint16_t channels = 16; /* With TFLite example, I have seen it 256 */
|
||||
int8_t *input, *output_c, *output_opt;
|
||||
const int size = input_wd * input_ht * channels;
|
||||
const int32_t activation_min = -128;
|
||||
const int32_t activation_max = 127;
|
||||
const uint16_t pad_wd = 1;
|
||||
const uint16_t pad_ht = 1;
|
||||
const uint16_t stride_wd = 1;
|
||||
const uint16_t stride_ht = 1;
|
||||
const uint16_t filter_ht = 3;
|
||||
const uint16_t filter_wd = 3;
|
||||
const uint16_t out_wd = input_wd / stride_wd;
|
||||
const uint16_t out_ht = input_ht / stride_ht;
|
||||
const int out_size = out_wd * out_ht * channels;
|
||||
|
||||
input = memalign(16, size);
|
||||
output_c = memalign(16, out_size);
|
||||
output_opt = memalign(16, out_size);
|
||||
|
||||
if (input == NULL || output_c == NULL || output_opt == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto max_pool_s8_cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input[i] = rand() % 256 - 128;
|
||||
}
|
||||
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
|
||||
/* C function */
|
||||
esp_nn_max_pool_s8_ansi(input, input_wd, input_ht, output_c, out_wd, out_ht,
|
||||
stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
|
||||
activation_min, activation_max, channels);
|
||||
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_max_pool_s8(input, input_wd, input_ht, output_opt, out_wd, out_ht,
|
||||
stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
|
||||
activation_min, activation_max, channels);
|
||||
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
|
||||
|
||||
bool ret = CHECK_EQUAL(output_c, output_opt, out_wd * out_ht * channels);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(output_opt, out_wd * out_ht * channels, 1);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(output_c, out_wd * out_ht * channels, 1);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, 8, size / 8);
|
||||
goto max_pool_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
|
||||
max_pool_s8_cleanup:
|
||||
if (input) {
|
||||
free(input);
|
||||
}
|
||||
if (output_c) {
|
||||
free(output_c);
|
||||
}
|
||||
if (output_opt) {
|
||||
free(output_opt);
|
||||
}
|
||||
}
|
||||
83
code/components/esp-nn/tests/src/relu_test.c
Normal file
83
code/components/esp-nn/tests/src/relu_test.c
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
void esp_nn_relu6_s8_test()
|
||||
{
|
||||
const int size = 1600 + 8 + 7;
|
||||
int8_t *input, *inout_ansi, *inout_opt;
|
||||
|
||||
input = memalign(16, size);
|
||||
inout_ansi = memalign(16, size);
|
||||
inout_opt = memalign(16, size);
|
||||
|
||||
if (input == NULL || inout_ansi == NULL || inout_opt == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto relu6_s8_cleanup;
|
||||
}
|
||||
/* Generate filter data between -128 -> +127 */
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input[i] = rand() % 255 - 128;
|
||||
inout_ansi[i] = input[i];
|
||||
inout_opt[i] = input[i];
|
||||
}
|
||||
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
|
||||
/* C function */
|
||||
esp_nn_relu6_s8_ansi(inout_ansi, size);
|
||||
|
||||
profile_c_end();
|
||||
profile_opt_start();
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_relu6_s8(inout_opt, size);
|
||||
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
|
||||
bool ret = CHECK_EQUAL(inout_ansi, inout_opt, size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(inout_opt, size, 1);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(inout_ansi, size, 1);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, size, 1);
|
||||
goto relu6_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
|
||||
relu6_s8_cleanup:
|
||||
if (input) {
|
||||
free (input);
|
||||
}
|
||||
if (inout_ansi) {
|
||||
free (inout_ansi);
|
||||
}
|
||||
if (inout_opt) {
|
||||
free (inout_opt);
|
||||
}
|
||||
|
||||
}
|
||||
101
code/components/esp-nn/tests/src/softmax_test.c
Normal file
101
code/components/esp-nn/tests/src/softmax_test.c
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2022 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <esp_nn.h>
|
||||
#include "test_utils.h"
|
||||
|
||||
void esp_nn_softmax_s8_test()
|
||||
{
|
||||
const int32_t height = 8;
|
||||
const int32_t width = 32;
|
||||
const int32_t diff_min = -128;
|
||||
const int32_t mult = INT32_MAX / 2;
|
||||
const int32_t shift = 7;
|
||||
void *scratch_buf = NULL;
|
||||
const int size = width * height;
|
||||
int8_t *input, *out_ansi, *out_opt;
|
||||
|
||||
input = memalign(16, size);
|
||||
out_ansi = memalign(16, size);
|
||||
out_opt = memalign(16, size);
|
||||
|
||||
if (input == NULL || out_ansi == NULL || out_opt == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s buffer allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
goto softmax_s8_cleanup;
|
||||
}
|
||||
|
||||
/* Generate input data between -128 -> +127 */
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input[i] = rand() % 255 - 128;
|
||||
}
|
||||
|
||||
/* enable profiler */
|
||||
profile_c_start();
|
||||
|
||||
/* C function */
|
||||
esp_nn_softmax_s8_ansi(input, height, width, mult, shift, diff_min, out_ansi);
|
||||
|
||||
profile_c_end();
|
||||
|
||||
int32_t scratch_buf_size = esp_nn_get_softmax_scratch_size(width, height);
|
||||
if (scratch_buf_size) {
|
||||
scratch_buf = memalign(4, scratch_buf_size);
|
||||
if (scratch_buf == NULL) {
|
||||
printf(ANSI_COLOR_RED"%s scratch_buf alloc failed size %d\n"ANSI_COLOR_RESET, __FUNCTION__, scratch_buf_size);
|
||||
goto softmax_s8_cleanup;
|
||||
}
|
||||
esp_nn_set_softmax_scratch_buf(scratch_buf);
|
||||
}
|
||||
|
||||
profile_opt_start();
|
||||
|
||||
/* Optimized function */
|
||||
esp_nn_softmax_s8(input, height, width, mult, shift, diff_min, out_opt);
|
||||
|
||||
/* disable profiler */
|
||||
profile_opt_end();
|
||||
|
||||
bool ret = CHECK_EQUAL(out_ansi, out_opt, size);
|
||||
if (ret == false) {
|
||||
printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
printf("Output: \n");
|
||||
PRINT_ARRAY_HEX(out_opt, width, height);
|
||||
printf("Expected: \n");
|
||||
PRINT_ARRAY_HEX(out_ansi, width, height);
|
||||
printf("Input:\n");
|
||||
PRINT_ARRAY_HEX(input, width, height);
|
||||
goto softmax_s8_cleanup;
|
||||
}
|
||||
printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
|
||||
|
||||
softmax_s8_cleanup:
|
||||
if (input) {
|
||||
free (input);
|
||||
}
|
||||
if (out_ansi) {
|
||||
free (out_ansi);
|
||||
}
|
||||
if (out_opt) {
|
||||
free (out_opt);
|
||||
}
|
||||
if (scratch_buf) {
|
||||
free (scratch_buf);
|
||||
}
|
||||
}
|
||||
BIN
code/components/esp-nn_20220827.zip
Normal file
BIN
code/components/esp-nn_20220827.zip
Normal file
Binary file not shown.
Binary file not shown.
@@ -3,8 +3,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- bugfix/*
|
||||
- feature/*
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
||||
21
code/components/esp32-camera-master/.github/workflows/upload_component.yml
vendored
Normal file
21
code/components/esp32-camera-master/.github/workflows/upload_component.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Push component to https://components.espressif.com
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
jobs:
|
||||
upload_components:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
submodules: "recursive"
|
||||
|
||||
- name: Upload component to the component registry
|
||||
uses: espressif/github-actions/upload_components@master
|
||||
with:
|
||||
name: "esp32-camera"
|
||||
version: "git"
|
||||
namespace: "espressif"
|
||||
service_url: ${{ secrets.IDF_COMPONENT_API_URL }}
|
||||
api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}
|
||||
@@ -13,6 +13,7 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
|
||||
sensors/gc0308.c
|
||||
sensors/gc2145.c
|
||||
sensors/gc032a.c
|
||||
sensors/bf3005.c
|
||||
conversions/yuv.c
|
||||
conversions/to_jpg.cpp
|
||||
conversions/to_bmp.c
|
||||
|
||||
@@ -62,6 +62,13 @@ menu "Camera configuration"
|
||||
help
|
||||
Enable this option if you want to use the GC0308.
|
||||
Disable this option to save memory.
|
||||
|
||||
config BF3005_SUPPORT
|
||||
bool "Support BF3005(BYD3005) VGA"
|
||||
default y
|
||||
help
|
||||
Enable this option if you want to use the BF3005.
|
||||
Disable this option to save memory.
|
||||
|
||||
choice SCCB_HARDWARE_I2C_PORT
|
||||
bool "I2C peripheral to use for SCCB"
|
||||
@@ -74,6 +81,14 @@ menu "Camera configuration"
|
||||
|
||||
endchoice
|
||||
|
||||
config SCCB_CLK_FREQ
|
||||
int "SCCB clk frequency"
|
||||
default 100000
|
||||
range 100000 400000
|
||||
help
|
||||
Increasing this value can reduce the initialization time of the sensor.
|
||||
Please refer to the relevant instructions of the sensor to adjust the value.
|
||||
|
||||
choice GC_SENSOR_WINDOW_MODE
|
||||
bool "GalaxyCore Sensor Window Mode"
|
||||
depends on (GC2145_SUPPORT || GC032A_SUPPORT || GC0308_SUPPORT)
|
||||
|
||||
@@ -24,6 +24,7 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi
|
||||
| GC032A | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/10" |
|
||||
| GC0308 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/6.5" |
|
||||
| GC2145 | 1600 x 1200 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/5" |
|
||||
| BF3005 | 640 x 480 | color | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565 | 1/4" |
|
||||
|
||||
## Important to Remember
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ static esp_err_t cam_dma_config(const camera_config_t *config)
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate memeory for frame buffer */
|
||||
/* Allocate memory for frame buffer */
|
||||
size_t alloc_size = fb_size * sizeof(uint8_t) + dma_align;
|
||||
uint32_t _caps = MALLOC_CAP_8BIT;
|
||||
if (CAMERA_FB_IN_DRAM == config->fb_location) {
|
||||
|
||||
@@ -54,7 +54,9 @@
|
||||
#if CONFIG_GC0308_SUPPORT
|
||||
#include "gc0308.h"
|
||||
#endif
|
||||
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
#include "bf3005.h"
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
@@ -114,6 +116,9 @@ static const sensor_func_t g_sensors[] = {
|
||||
#if CONFIG_GC0308_SUPPORT
|
||||
{gc0308_detect, gc0308_init},
|
||||
#endif
|
||||
#if CONFIG_BF3005_SUPPORT
|
||||
{bf3005_detect, bf3005_init},
|
||||
#endif
|
||||
};
|
||||
|
||||
static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
|
||||
|
||||
@@ -26,6 +26,7 @@ typedef enum {
|
||||
GC2145_PID = 0x2145,
|
||||
GC032A_PID = 0x232a,
|
||||
GC0308_PID = 0x9b,
|
||||
BF3005_PID = 0x30,
|
||||
} camera_pid_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -38,6 +39,7 @@ typedef enum {
|
||||
CAMERA_GC2145,
|
||||
CAMERA_GC032A,
|
||||
CAMERA_GC0308,
|
||||
CAMERA_BF3005,
|
||||
CAMERA_MODEL_MAX,
|
||||
CAMERA_NONE,
|
||||
} camera_model_t;
|
||||
@@ -52,6 +54,7 @@ typedef enum {
|
||||
GC2145_SCCB_ADDR = 0x3C,// 0x78 >> 1
|
||||
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
|
||||
BF3005_SCCB_ADDR = 0x6E,
|
||||
} camera_sccb_addr_t;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -25,13 +25,13 @@ static const char* TAG = "sccb";
|
||||
|
||||
#include "driver/i2c.h"
|
||||
|
||||
#define SCCB_FREQ 100000 /*!< I2C master frequency*/
|
||||
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||
#define SCCB_FREQ CONFIG_SCCB_CLK_FREQ /*!< I2C master frequency*/
|
||||
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
||||
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
||||
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
||||
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
||||
#define ACK_VAL 0x0 /*!< I2C ack value */
|
||||
#define NACK_VAL 0x1 /*!< I2C nack value */
|
||||
#if CONFIG_SCCB_HARDWARE_I2C_PORT1
|
||||
const int SCCB_I2C_PORT = 1;
|
||||
#else
|
||||
|
||||
@@ -12,6 +12,7 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
|
||||
{CAMERA_GC2145, "GC2145", GC2145_SCCB_ADDR, GC2145_PID, FRAMESIZE_UXGA, false},
|
||||
{CAMERA_GC032A, "GC032A", GC032A_SCCB_ADDR, GC032A_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_GC0308, "GC0308", GC0308_SCCB_ADDR, GC0308_PID, FRAMESIZE_VGA, false},
|
||||
{CAMERA_BF3005, "BF3005", BF3005_SCCB_ADDR, BF3005_PID, FRAMESIZE_VGA, false},
|
||||
};
|
||||
|
||||
const resolution_info_t resolution[FRAMESIZE_INVALID] = {
|
||||
|
||||
5
code/components/esp32-camera-master/idf_component.yml
Normal file
5
code/components/esp32-camera-master/idf_component.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
description: ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors.
|
||||
targets:
|
||||
- esp32
|
||||
- esp32s2
|
||||
- esp32s3
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "esp32-camera",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.0",
|
||||
"keywords": "esp32, camera, espressif, esp32-cam",
|
||||
"description": "ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors.",
|
||||
"repository": {
|
||||
|
||||
541
code/components/esp32-camera-master/sensors/bf3005.c
Normal file
541
code/components/esp32-camera-master/sensors/bf3005.c
Normal file
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* BF3005 driver.
|
||||
*
|
||||
* Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "sccb.h"
|
||||
#include "xclk.h"
|
||||
#include "bf3005.h"
|
||||
#include "bf3005_regs.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
|
||||
#include "esp32-hal-log.h"
|
||||
#else
|
||||
#include "esp_log.h"
|
||||
static const char* TAG = "bf3005";
|
||||
#endif
|
||||
|
||||
static const uint8_t default_regs[][2] = {
|
||||
{0x12, 0x40}, //soft reset
|
||||
{0xff, 0xff}, //delay
|
||||
{0xff, 0xff}, //delay
|
||||
{0xff, 0xff}, //delay
|
||||
{0xff, 0xff}, //delay
|
||||
{0x13, 0x10},
|
||||
{0x8c, 0x00},
|
||||
{0x8d, 0x64},
|
||||
{0x87, 0x10},
|
||||
{0x13, 0x17},
|
||||
{0x00, 0x20},
|
||||
{0x01, 0x1a},
|
||||
{0x02, 0x22},
|
||||
{0x09, 0x03},
|
||||
{0x0c, 0x80},
|
||||
{0x0d, 0x24},
|
||||
{0x0e, 0x21},
|
||||
{0x0f, 0x28},
|
||||
{0x11, 0x08},
|
||||
{0x15, 0x10}, // 0X10
|
||||
{0x16, 0x03},
|
||||
{0x1e, 0x30},
|
||||
{0x20, 0x8a},
|
||||
{0x21, 0x03},
|
||||
{0x23, 0x55},
|
||||
{0x24, 0x68},
|
||||
{0x25, 0x78},
|
||||
{0x2a, 0x00},
|
||||
{0x2b, 0x00},
|
||||
{0x2d, 0x4f},
|
||||
{0x2e, 0x98},
|
||||
{0x2f, 0x04},
|
||||
{0x30, 0xad},
|
||||
{0x31, 0x17},
|
||||
{0x32, 0x6e},
|
||||
{0x33, 0x20},
|
||||
{0x35, 0xa6},
|
||||
{0x3b, 0x00},
|
||||
{0x3e, 0x00},
|
||||
{0x3f, 0xA8},
|
||||
{0x40, 0x38},
|
||||
{0x41, 0x32},
|
||||
{0x42, 0x2b},
|
||||
{0x43, 0x26},
|
||||
{0x44, 0x1a},
|
||||
{0x45, 0x16},
|
||||
{0x46, 0x10},
|
||||
{0x47, 0x0f},
|
||||
{0x48, 0x0c},
|
||||
{0x49, 0x0a},
|
||||
{0x4b, 0x09},
|
||||
{0x4c, 0x08},
|
||||
{0x4d, 0x3c},
|
||||
{0x4e, 0x06},
|
||||
{0x4f, 0x05},
|
||||
{0x50, 0x03},
|
||||
{0x51, 0x25},
|
||||
{0x52, 0x88},
|
||||
{0x53, 0x03},
|
||||
{0x63, 0x20},
|
||||
{0x64, 0x02},
|
||||
{0x65, 0xa6},
|
||||
{0x66, 0xb6},
|
||||
{0x69, 0x00},
|
||||
{0x70, 0xFF},
|
||||
{0x71, 0xa6},
|
||||
{0x72, 0x2f},
|
||||
{0x73, 0x2f},
|
||||
{0x74, 0x2F},
|
||||
{0x75, 0x0e},
|
||||
{0x76, 0x1e},
|
||||
{0x77, 0x00},
|
||||
{0x78, 0x1e},
|
||||
{0x79, 0x8a},
|
||||
{0x7d, 0xe2},
|
||||
{0x80, 0x44},
|
||||
{0x81, 0x00},
|
||||
{0x82, 0x18},
|
||||
{0x83, 0x1b},
|
||||
{0x84, 0x24},
|
||||
{0x85, 0x2a},
|
||||
{0x86, 0x4f},
|
||||
{0x89, 0x82}, //0x82
|
||||
{0x8b, 0x02},
|
||||
{0x8e, 0x03},
|
||||
{0x8f, 0xFC},
|
||||
{0x9d, 0x4d},
|
||||
{0x9e, 0x41},
|
||||
{0xa1, 0x21},
|
||||
{0xa2, 0x12},
|
||||
{0xa3, 0x32},
|
||||
{0xa4, 0x05},
|
||||
{0xa5, 0x32},
|
||||
{0xa6, 0x04},
|
||||
{0xa7, 0x7f},
|
||||
{0xa8, 0x7f},
|
||||
{0xa9, 0x21},
|
||||
{0xaa, 0x21},
|
||||
{0xab, 0x21},
|
||||
{0xac, 0x0a},
|
||||
{0xad, 0xf0},
|
||||
{0xae, 0xff},
|
||||
{0xaf, 0x1d},
|
||||
{0xb0, 0x94},
|
||||
{0xb1, 0xc0},
|
||||
{0xb2, 0xc0},
|
||||
{0xd2, 0x30},
|
||||
{0xe0, 0x0d},
|
||||
{0xe1, 0x44},
|
||||
{0xe7, 0x7c},
|
||||
{0xe8, 0x89},
|
||||
{0xe9, 0x01},
|
||||
{0xea, 0x01},
|
||||
{0xf0, 0x01},
|
||||
{0xf3, 0x49},
|
||||
{0xf4, 0xff},
|
||||
{0xf5, 0x01},
|
||||
{0xf6, 0xf2},
|
||||
{0xf7, 0x6f},
|
||||
{0x1b, 0x80},
|
||||
{0x00, 0x00},
|
||||
};
|
||||
|
||||
static int get_reg(sensor_t *sensor, int reg, int mask)
|
||||
{
|
||||
int ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
|
||||
if(ret > 0){
|
||||
ret &= mask;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Read(sensor->slv_addr, reg & 0xFF);
|
||||
if(ret < 0){
|
||||
return ret;
|
||||
}
|
||||
value = (ret & ~mask) | (value & mask);
|
||||
ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Read(sensor->slv_addr, reg);
|
||||
if(ret < 0){
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
value = (ret & ~mask) | ((value << offset) & mask);
|
||||
ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Read(sensor->slv_addr, reg);
|
||||
if(ret < 0){
|
||||
return ret;
|
||||
}
|
||||
uint8_t mask = ((1 << length) - 1) << offset;
|
||||
return (ret & mask) >> offset;
|
||||
}
|
||||
|
||||
|
||||
static int reset(sensor_t *sensor)
|
||||
{
|
||||
int i=0;
|
||||
const uint8_t (*regs)[2];
|
||||
|
||||
// Write default regsiters
|
||||
for (i=0, regs = default_regs; regs[i][0]; i++) {
|
||||
SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
|
||||
}
|
||||
|
||||
// Delay
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
|
||||
{
|
||||
int ret=0;
|
||||
sensor->pixformat = pixformat;
|
||||
|
||||
switch (pixformat) {
|
||||
case PIXFORMAT_RGB565:
|
||||
set_reg_bits(sensor, 0x12, 2, 1, 1);
|
||||
break;
|
||||
case PIXFORMAT_RAW:
|
||||
set_reg_bits(sensor, 0x12, 0, 3, 0x4);
|
||||
break;
|
||||
case PIXFORMAT_YUV422:
|
||||
case PIXFORMAT_GRAYSCALE:
|
||||
set_reg_bits(sensor, 0x12, 2, 1, 0);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Delay
|
||||
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
{
|
||||
int ret=0;
|
||||
if (framesize > FRAMESIZE_VGA) {
|
||||
return -1;
|
||||
}
|
||||
uint16_t w = resolution[framesize].width;
|
||||
uint16_t h = resolution[framesize].height;
|
||||
// uint8_t reg = SCCB_Read(sensor->slv_addr, COM7);
|
||||
|
||||
sensor->status.framesize = framesize;
|
||||
|
||||
// Write MSBs
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, 0);
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, w>>2);
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, 0);
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, h>>2);
|
||||
|
||||
// Write LSBs
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x03, 0);
|
||||
printf("%s %d\r\n", __func__, __LINE__);
|
||||
if((w<=320)&&(h<=240))
|
||||
{
|
||||
printf("%s %d\r\n", __func__, __LINE__);
|
||||
// Enable auto-scaling/zooming factors
|
||||
//ret |= SCCB_Write(sensor->slv_addr, 0x12, 0x50);
|
||||
set_reg_bits(sensor, 0x12, 4, 1, 1);
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, (80-w/4));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, (80+w/4));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, (60-h/4));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60+h/4));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x03, 0);
|
||||
|
||||
} else if((w<=640)&&(h<=480))
|
||||
{
|
||||
// Enable auto-scaling/zooming factors
|
||||
//ret |= SCCB_Write(sensor->slv_addr, 0x12, 0x40);
|
||||
set_reg_bits(sensor, 0x12, 4, 1, 0);
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x17, (80-w/8));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x18, (80+w/8));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x19, (60-h/8));
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60+h/8));
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0x03, 0);
|
||||
}
|
||||
|
||||
// Delay
|
||||
vTaskDelay(30 / portTICK_PERIOD_MS);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_colorbar(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret=0;
|
||||
sensor->status.colorbar = value;
|
||||
|
||||
ret |= SCCB_Write(sensor->slv_addr, 0xb9, value);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_whitebal(sensor_t *sensor, int enable)
|
||||
{
|
||||
if(set_reg_bits(sensor, 0x13, 1, 1, enable) >= 0){
|
||||
sensor->status.awb = !!enable;
|
||||
}
|
||||
return sensor->status.awb;
|
||||
}
|
||||
|
||||
|
||||
static int set_gain_ctrl(sensor_t *sensor, int enable)
|
||||
{
|
||||
if(set_reg_bits(sensor, 0x13, 2, 1, enable) >= 0){
|
||||
sensor->status.agc = !!enable;
|
||||
}
|
||||
return sensor->status.agc;
|
||||
}
|
||||
|
||||
|
||||
static int set_exposure_ctrl(sensor_t *sensor, int enable)
|
||||
{
|
||||
if(set_reg_bits(sensor, 0x13, 0, 1, enable) >= 0){
|
||||
sensor->status.aec = !!enable;
|
||||
}
|
||||
return sensor->status.aec;
|
||||
}
|
||||
|
||||
static int set_hmirror(sensor_t *sensor, int enable)
|
||||
{
|
||||
if(set_reg_bits(sensor, 0x1e, 5, 1, enable) >= 0){
|
||||
sensor->status.hmirror = !!enable;
|
||||
}
|
||||
return sensor->status.hmirror;
|
||||
}
|
||||
|
||||
static int set_vflip(sensor_t *sensor, int enable)
|
||||
{
|
||||
if(set_reg_bits(sensor, 0x1e, 4, 1, enable) >= 0){
|
||||
sensor->status.vflip = !!enable;
|
||||
}
|
||||
return sensor->status.vflip;
|
||||
}
|
||||
|
||||
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = set_reg_bits(sensor, 0xf1, 1, 1, !enable);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set raw_gma to: %d", !enable);
|
||||
sensor->status.raw_gma = !enable;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int set_lenc_dsp(sensor_t *sensor, int enable)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = set_reg_bits(sensor, 0xf1, 0, 1, !enable);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set lenc to: %d", !enable);
|
||||
sensor->status.lenc = !enable;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_agc_gain(sensor_t *sensor, int option)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = set_reg_bits(sensor, 0x13, 4, 1, !!option);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set gain to: %d", !!option);
|
||||
sensor->status.agc_gain = !!option;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_awb_gain_dsp(sensor_t *sensor, int value)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Write(sensor->slv_addr, 0xa6, value);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set awb gain threthold to: %d", value);
|
||||
sensor->status.awb_gain = value;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_brightness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Write(sensor->slv_addr, 0x55, level);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set brightness to: %d", level);
|
||||
sensor->status.brightness = level;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_contrast(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Write(sensor->slv_addr, 0x56, level);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set contrast to: %d", level);
|
||||
sensor->status.contrast = level;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_sharpness(sensor_t *sensor, int level)
|
||||
{
|
||||
int ret = 0;
|
||||
ret = SCCB_Write(sensor->slv_addr, 0x70, level);
|
||||
if (ret == 0) {
|
||||
ESP_LOGD(TAG, "Set sharpness to: %d", level);
|
||||
sensor->status.sharpness = level;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_status(sensor_t *sensor)
|
||||
{
|
||||
sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x55);
|
||||
sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0x56);
|
||||
sensor->status.saturation = 0;
|
||||
sensor->status.ae_level = 0;
|
||||
|
||||
sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x87);
|
||||
sensor->status.awb = get_reg_bits(sensor, 0x13, 1, 1);
|
||||
sensor->status.awb_gain = SCCB_Read(sensor->slv_addr, 0xa6);
|
||||
sensor->status.aec = get_reg_bits(sensor, 0x13, 0, 1);
|
||||
|
||||
sensor->status.agc = get_reg_bits(sensor, 0x13, 2, 1);
|
||||
|
||||
sensor->status.raw_gma = get_reg_bits(sensor, 0xf1, 1, 1);
|
||||
sensor->status.lenc = get_reg_bits(sensor, 0xf1, 0, 1);
|
||||
sensor->status.hmirror = get_reg_bits(sensor, 0x1e, 5, 1);
|
||||
sensor->status.vflip = get_reg_bits(sensor, 0x1e, 4, 1);
|
||||
|
||||
sensor->status.colorbar = SCCB_Read(sensor->slv_addr, 0xb9);
|
||||
sensor->status.sharpness = SCCB_Read(sensor->slv_addr, 0x70);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dummy(sensor_t *sensor, int val){ return -1; }
|
||||
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
|
||||
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning){return -1;}
|
||||
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div){return -1;}
|
||||
|
||||
static int set_xclk(sensor_t *sensor, int timer, int xclk)
|
||||
{
|
||||
int ret = 0;
|
||||
sensor->xclk_freq_hz = xclk * 1000000U;
|
||||
ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bf3005_detect(int slv_addr, sensor_id_t *id)
|
||||
{
|
||||
if (BF3005_SCCB_ADDR == slv_addr) {
|
||||
uint16_t PID = SCCB_Read(slv_addr, 0xFC);
|
||||
if (BF3005_PID == PID) {
|
||||
id->PID = PID;
|
||||
id->VER = SCCB_Read(slv_addr, 0xFD);
|
||||
id->MIDL = SCCB_Read(slv_addr, 0xFC);
|
||||
id->MIDH = SCCB_Read(slv_addr, 0xFD);
|
||||
return PID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bf3005_init(sensor_t *sensor)
|
||||
{
|
||||
// Set function pointers
|
||||
sensor->reset = reset;
|
||||
sensor->init_status = init_status;
|
||||
sensor->set_pixformat = set_pixformat;
|
||||
sensor->set_framesize = set_framesize;
|
||||
sensor->set_brightness = set_brightness;
|
||||
sensor->set_contrast = set_contrast;
|
||||
|
||||
sensor->set_colorbar = set_colorbar;
|
||||
|
||||
sensor->set_gain_ctrl = set_gain_ctrl;
|
||||
sensor->set_exposure_ctrl = set_exposure_ctrl;
|
||||
sensor->set_hmirror = set_hmirror;
|
||||
sensor->set_vflip = set_vflip;
|
||||
|
||||
sensor->set_whitebal = set_whitebal;
|
||||
|
||||
sensor->set_awb_gain = set_awb_gain_dsp;
|
||||
sensor->set_agc_gain = set_agc_gain;
|
||||
|
||||
sensor->set_raw_gma = set_raw_gma_dsp;
|
||||
sensor->set_lenc = set_lenc_dsp;
|
||||
|
||||
sensor->set_sharpness = set_sharpness;
|
||||
//not supported
|
||||
sensor->set_saturation= set_dummy;
|
||||
sensor->set_denoise = set_dummy;
|
||||
sensor->set_quality = set_dummy;
|
||||
sensor->set_special_effect = set_dummy;
|
||||
sensor->set_wb_mode = set_dummy;
|
||||
sensor->set_ae_level = set_dummy;
|
||||
sensor->set_gainceiling = set_gainceiling_dummy;
|
||||
|
||||
|
||||
sensor->get_reg = get_reg;
|
||||
sensor->set_reg = set_reg;
|
||||
sensor->set_res_raw = set_res_raw;
|
||||
sensor->set_pll = _set_pll;
|
||||
sensor->set_xclk = set_xclk;
|
||||
|
||||
ESP_LOGD(TAG, "BF3005 Attached");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -184,6 +184,8 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
uint16_t h = resolution[framesize].height;
|
||||
uint16_t row_s = (resolution[FRAMESIZE_VGA].height - h) / 2;
|
||||
uint16_t col_s = (resolution[FRAMESIZE_VGA].width - w) / 2;
|
||||
(void)row_s;
|
||||
(void)col_s;
|
||||
|
||||
#if CONFIG_GC_SENSOR_SUBSAMPLE_MODE
|
||||
struct subsample_cfg {
|
||||
|
||||
@@ -190,6 +190,8 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
|
||||
uint16_t h = resolution[framesize].height;
|
||||
uint16_t row_s = (resolution[FRAMESIZE_UXGA].height - h) / 2;
|
||||
uint16_t col_s = (resolution[FRAMESIZE_UXGA].width - w) / 2;
|
||||
(void)row_s;
|
||||
(void)col_s;
|
||||
|
||||
#if CONFIG_GC_SENSOR_SUBSAMPLE_MODE
|
||||
struct subsample_cfg {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* BF3005 driver.
|
||||
*
|
||||
*/
|
||||
#ifndef __BF3005_H__
|
||||
#define __BF3005_H__
|
||||
#include "sensor.h"
|
||||
|
||||
/**
|
||||
* @brief Detect sensor pid
|
||||
*
|
||||
* @param slv_addr SCCB address
|
||||
* @param id Detection result
|
||||
* @return
|
||||
* 0: Can't detect this sensor
|
||||
* Nonzero: This sensor has been detected
|
||||
*/
|
||||
int bf3005_detect(int slv_addr, sensor_id_t *id);
|
||||
|
||||
/**
|
||||
* @brief initialize sensor function pointers
|
||||
*
|
||||
* @param sensor pointer of sensor
|
||||
* @return
|
||||
* Always 0
|
||||
*/
|
||||
int bf3005_init(sensor_t *sensor);
|
||||
|
||||
#endif // __BF3005_H__
|
||||
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* BF3005 register definitions.
|
||||
*/
|
||||
#ifndef __REG_REGS_H__
|
||||
#define __REG_REGS_H__
|
||||
#if 0
|
||||
#define GAIN 0x00 /* AGC <20>C Gain control gain setting */
|
||||
#define BLUE 0x01 /* AWB <20>C Blue channel gain setting */
|
||||
#define RED 0x02 /* AWB <20>C Red channel gain setting */
|
||||
#define GREEN 0x03 /* AWB <20>C Green channel gain setting */
|
||||
#define BAVG 0x05 /* U/B Average Level */
|
||||
#define GAVG 0x06 /* Y/Gb Average Level */
|
||||
#define RAVG 0x07 /* V/R Average Level */
|
||||
#define AECH 0x08 /* Exposure Value <20>C AEC MSBs */
|
||||
|
||||
#define COM2 0x09 /* Common Control 2 */
|
||||
#define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */
|
||||
#define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */
|
||||
#define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */
|
||||
#define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */
|
||||
#define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */
|
||||
|
||||
#define REG_PID 0x0A /* Product ID Number MSB */
|
||||
#define REG_VER 0x0B /* Product ID Number LSB */
|
||||
|
||||
#define COM3 0x0C /* Common Control 3 */
|
||||
#define COM3_VFLIP 0x80 /* Vertical flip image ON/OFF selection */
|
||||
#define COM3_MIRROR 0x40 /* Horizontal mirror image ON/OFF selection */
|
||||
#define COM3_SWAP_BR 0x20 /* Swap B/R output sequence in RGB output mode */
|
||||
#define COM3_SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV output mode */
|
||||
#define COM3_SWAP_MSB 0x08 /* Swap output MSB/LSB */
|
||||
#define COM3_TRI_CLOCK 0x04 /* Tri-state option for output clock at power-down period */
|
||||
#define COM3_TRI_DATA 0x02 /* Tri-state option for output data at power-down period */
|
||||
#define COM3_COLOR_BAR 0x01 /* Sensor color bar test pattern output enable */
|
||||
#define COM3_SET_CBAR(r, x) ((r&0xFE)|((x&1)<<0))
|
||||
#define COM3_SET_MIRROR(r, x) ((r&0xBF)|((x&1)<<6))
|
||||
#define COM3_SET_FLIP(r, x) ((r&0x7F)|((x&1)<<7))
|
||||
|
||||
#define COM4 0x0D /* Common Control 4 */
|
||||
#define COM4_PLL_BYPASS 0x00 /* Bypass PLL */
|
||||
#define COM4_PLL_4x 0x40 /* PLL frequency 4x */
|
||||
#define COM4_PLL_6x 0x80 /* PLL frequency 6x */
|
||||
#define COM4_PLL_8x 0xc0 /* PLL frequency 8x */
|
||||
#define COM4_AEC_FULL 0x00 /* AEC evaluate full window */
|
||||
#define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */
|
||||
#define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */
|
||||
#define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */
|
||||
|
||||
#define COM5 0x0E /* Common Control 5 */
|
||||
#define COM5_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */
|
||||
#define COM5_AFR_SPEED 0x40 /* Auto frame rate control speed selection */
|
||||
#define COM5_AFR_0 0x00 /* No reduction of frame rate */
|
||||
#define COM5_AFR_1_2 0x10 /* Max reduction to 1/2 frame rate */
|
||||
#define COM5_AFR_1_4 0x20 /* Max reduction to 1/4 frame rate */
|
||||
#define COM5_AFR_1_8 0x30 /* Max reduction to 1/8 frame rate */
|
||||
#define COM5_AFR_4x 0x04 /* Add frame when AGC reaches 4x gain */
|
||||
#define COM5_AFR_8x 0x08 /* Add frame when AGC reaches 8x gain */
|
||||
#define COM5_AFR_16x 0x0c /* Add frame when AGC reaches 16x gain */
|
||||
#define COM5_AEC_NO_LIMIT 0x01 /* No limit to AEC increase step */
|
||||
|
||||
#define COM6 0x0F /* Common Control 6 */
|
||||
#define COM6_AUTO_WINDOW 0x01 /* Auto window setting ON/OFF selection when format changes */
|
||||
|
||||
#define AEC 0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */
|
||||
#define CLKRC 0x11 /* Internal Clock */
|
||||
|
||||
#define COM7 0x12 /* Common Control 7 */
|
||||
#define COM7_RESET 0x80 /* SCCB Register Reset */
|
||||
#define COM7_RES_VGA 0x00 /* Resolution VGA */
|
||||
#define COM7_RES_QVGA 0x40 /* Resolution QVGA */
|
||||
#define COM7_BT656 0x20 /* BT.656 protocol ON/OFF */
|
||||
#define COM7_SENSOR_RAW 0x10 /* Sensor RAW */
|
||||
#define COM7_FMT_GBR422 0x00 /* RGB output format GBR422 */
|
||||
#define COM7_FMT_RGB565 0x04 /* RGB output format RGB565 */
|
||||
#define COM7_FMT_RGB555 0x08 /* RGB output format RGB555 */
|
||||
#define COM7_FMT_RGB444 0x0C /* RGB output format RGB444 */
|
||||
#define COM7_FMT_YUV 0x00 /* Output format YUV */
|
||||
#define COM7_FMT_P_BAYER 0x01 /* Output format Processed Bayer RAW */
|
||||
#define COM7_FMT_RGB 0x02 /* Output format RGB */
|
||||
#define COM7_FMT_R_BAYER 0x03 /* Output format Bayer RAW */
|
||||
#define COM7_SET_FMT(r, x) ((r&0xFC)|((x&0x3)<<0))
|
||||
#define COM7_SET_RGB(r, x) ((r&0xF0)|(x&0x0C)|COM7_FMT_RGB)
|
||||
|
||||
#define COM8 0x13 /* Common Control 8 */
|
||||
#define COM8_FAST_AUTO 0x80 /* Enable fast AGC/AEC algorithm */
|
||||
#define COM8_STEP_VSYNC 0x00 /* AEC - Step size limited to vertical blank */
|
||||
#define COM8_STEP_UNLIMIT 0x40 /* AEC - Step size unlimited step size */
|
||||
#define COM8_BANDF_EN 0x20 /* Banding filter ON/OFF */
|
||||
#define COM8_AEC_BANDF 0x10 /* Enable AEC below banding value */
|
||||
#define COM8_AEC_FINE_EN 0x08 /* Fine AEC ON/OFF control */
|
||||
#define COM8_AGC_EN 0x04 /* AGC Enable */
|
||||
#define COM8_AWB_EN 0x02 /* AWB Enable */
|
||||
#define COM8_AEC_EN 0x01 /* AEC Enable */
|
||||
#define COM8_SET_AGC(r, x) ((r&0xFB)|((x&0x1)<<2))
|
||||
#define COM8_SET_AWB(r, x) ((r&0xFD)|((x&0x1)<<1))
|
||||
#define COM8_SET_AEC(r, x) ((r&0xFE)|((x&0x1)<<0))
|
||||
|
||||
#define COM9 0x14 /* Common Control 9 */
|
||||
#define COM9_HISTO_AVG 0x80 /* Histogram or average based AEC/AGC selection */
|
||||
#define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */
|
||||
#define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */
|
||||
#define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */
|
||||
#define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */
|
||||
#define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */
|
||||
#define COM9_DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
|
||||
#define COM9_DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
|
||||
#define COM9_SET_AGC(r, x) ((r&0x8F)|((x&0x07)<<4))
|
||||
|
||||
#define COM10 0x15 /* Common Control 10 */
|
||||
#define COM10_NEGATIVE 0x80 /* Output negative data */
|
||||
#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */
|
||||
#define COM10_PCLK_FREE 0x00 /* PCLK output option: free running PCLK */
|
||||
#define COM10_PCLK_MASK 0x20 /* PCLK output option: masked during horizontal blank */
|
||||
#define COM10_PCLK_REV 0x10 /* PCLK reverse */
|
||||
#define COM10_HREF_REV 0x08 /* HREF reverse */
|
||||
#define COM10_VSYNC_FALLING 0x00 /* VSYNC changes on falling edge of PCLK */
|
||||
#define COM10_VSYNC_RISING 0x04 /* VSYNC changes on rising edge of PCLK */
|
||||
#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */
|
||||
#define COM10_OUT_RANGE_8 0x01 /* Output data range: Full range */
|
||||
#define COM10_OUT_RANGE_10 0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */
|
||||
|
||||
#define REG16 0x16 /* Register 16 */
|
||||
#define REG16_BIT_SHIFT 0x80 /* Bit shift test pattern options */
|
||||
#define HSTART 0x17 /* Horizontal Frame (HREF column) Start 8 MSBs (2 LSBs are at HREF[5:4]) */
|
||||
#define HSIZE 0x18 /* Horizontal Sensor Size (2 LSBs are at HREF[1:0]) */
|
||||
#define VSTART 0x19 /* Vertical Frame (row) Start 8 MSBs (1 LSB is at HREF[6]) */
|
||||
#define VSIZE 0x1A /* Vertical Sensor Size (1 LSB is at HREF[2]) */
|
||||
#define PSHFT 0x1B /* Data Format - Pixel Delay Select */
|
||||
#define REG_MIDH 0x1C /* Manufacturer ID Byte <20>C High */
|
||||
#define REG_MIDL 0x1D /* Manufacturer ID Byte <20>C Low */
|
||||
#define LAEC 0x1F /* Fine AEC Value - defines exposure value less than one row period */
|
||||
|
||||
#define COM11 0x20 /* Common Control 11 */
|
||||
#define COM11_SNGL_FRAME_EN 0x02 /* Single frame ON/OFF selection */
|
||||
#define COM11_SNGL_XFR_TRIG 0x01 /* Single frame transfer trigger */
|
||||
|
||||
#define BDBASE 0x22 /* Banding Filter Minimum AEC Value */
|
||||
#define DBSTEP 0x23 /* Banding Filter Maximum Step */
|
||||
#define AEW 0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */
|
||||
#define AEB 0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */
|
||||
#define VPT 0x26 /* AGC/AEC Fast Mode Operating Region */
|
||||
#define REG28 0x28 /* Selection on the number of dummy rows, N */
|
||||
#define HOUTSIZE 0x29 /* Horizontal Data Output Size MSBs (2 LSBs at register EXHCH[1:0]) */
|
||||
#define EXHCH 0x2A /* Dummy Pixel Insert MSB */
|
||||
#define EXHCL 0x2B /* Dummy Pixel Insert LSB */
|
||||
#define VOUTSIZE 0x2C /* Vertical Data Output Size MSBs (LSB at register EXHCH[2]) */
|
||||
#define ADVFL 0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row) */
|
||||
#define ADVFH 0x2E /* MSB of Insert Dummy Rows in Vertical Sync */
|
||||
#define YAVE 0x2F /* Y/G Channel Average Value */
|
||||
#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance High Level Threshold */
|
||||
#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance Low Level Threshold */
|
||||
#define HREF 0x32 /* Image Start and Size Control */
|
||||
#define DM_LNL 0x33 /* Dummy Row Low 8 Bits */
|
||||
#define DM_LNH 0x34 /* Dummy Row High 8 Bits */
|
||||
#define ADOFF_B 0x35 /* AD Offset Compensation Value for B Channel */
|
||||
#define ADOFF_R 0x36 /* AD Offset Compensation Value for R Channel */
|
||||
#define ADOFF_GB 0x37 /* AD Offset Compensation Value for GB Channel */
|
||||
#define ADOFF_GR 0x38 /* AD Offset Compensation Value for GR Channel */
|
||||
#define OFF_B 0x39 /* AD Offset Compensation Value for B Channel */
|
||||
#define OFF_R 0x3A /* AD Offset Compensation Value for R Channel */
|
||||
#define OFF_GB 0x3B /* AD Offset Compensation Value for GB Channel */
|
||||
#define OFF_GR 0x3C /* AD Offset Compensation Value for GR Channel */
|
||||
#define COM12 0x3D /* DC offset compensation for analog process */
|
||||
|
||||
#define COM13 0x3E /* Common Control 13 */
|
||||
#define COM13_BLC_EN 0x80 /* BLC enable */
|
||||
#define COM13_ADC_EN 0x40 /* ADC channel BLC ON/OFF control */
|
||||
#define COM13_ANALOG_BLC 0x20 /* Analog processing channel BLC ON/OFF control */
|
||||
#define COM13_ABLC_GAIN_EN 0x04 /* ABLC gain trigger enable */
|
||||
|
||||
#define COM14 0x3F /* Common Control 14 */
|
||||
#define COM15 0x40 /* Common Control 15 */
|
||||
#define COM16 0x41 /* Common Control 16 */
|
||||
#define TGT_B 0x42 /* BLC Blue Channel Target Value */
|
||||
#define TGT_R 0x43 /* BLC Red Channel Target Value */
|
||||
#define TGT_GB 0x44 /* BLC Gb Channel Target Value */
|
||||
#define TGT_GR 0x45 /* BLC Gr Channel Target Value */
|
||||
|
||||
#define LC_CTR 0x46 /* Lens Correction Control */
|
||||
#define LC_CTR_RGB_COMP_1 0x00 /* R, G, and B channel compensation coefficient is set by LC_COEF (0x49) */
|
||||
#define LC_CTR_RGB_COMP_3 0x04 /* R, G, and B channel compensation coefficient is set by registers
|
||||
LC_COEFB (0x4B), LC_COEF (0x49), and LC_COEFR (0x4C), respectively */
|
||||
#define LC_CTR_EN 0x01 /* Lens correction enable */
|
||||
#define LC_XC 0x47 /* X Coordinate of Lens Correction Center Relative to Array Center */
|
||||
#define LC_YC 0x48 /* Y Coordinate of Lens Correction Center Relative to Array Center */
|
||||
#define LC_COEF 0x49 /* Lens Correction Coefficient */
|
||||
#define LC_RADI 0x4A /* Lens Correction Radius */
|
||||
#define LC_COEFB 0x4B /* Lens Correction B Channel Compensation Coefficient */
|
||||
#define LC_COEFR 0x4C /* Lens Correction R Channel Compensation Coefficient */
|
||||
|
||||
#define FIXGAIN 0x4D /* Analog Fix Gain Amplifier */
|
||||
#define AREF0 0x4E /* Sensor Reference Control */
|
||||
#define AREF1 0x4F /* Sensor Reference Current Control */
|
||||
#define AREF2 0x50 /* Analog Reference Control */
|
||||
#define AREF3 0x51 /* ADC Reference Control */
|
||||
#define AREF4 0x52 /* ADC Reference Control */
|
||||
#define AREF5 0x53 /* ADC Reference Control */
|
||||
#define AREF6 0x54 /* Analog Reference Control */
|
||||
#define AREF7 0x55 /* Analog Reference Control */
|
||||
#define UFIX 0x60 /* U Channel Fixed Value Output */
|
||||
#define VFIX 0x61 /* V Channel Fixed Value Output */
|
||||
#define AWBB_BLK 0x62 /* AWB Option for Advanced AWB */
|
||||
|
||||
#define AWB_CTRL0 0x63 /* AWB Control Byte 0 */
|
||||
#define AWB_CTRL0_GAIN_EN 0x80 /* AWB gain enable */
|
||||
#define AWB_CTRL0_CALC_EN 0x40 /* AWB calculate enable */
|
||||
#define AWB_CTRL0_WBC_MASK 0x0F /* WBC threshold 2 */
|
||||
|
||||
#define DSP_CTRL1 0x64 /* DSP Control Byte 1 */
|
||||
#define DSP_CTRL1_FIFO_EN 0x80 /* FIFO enable/disable selection */
|
||||
#define DSP_CTRL1_UV_EN 0x40 /* UV adjust function ON/OFF selection */
|
||||
#define DSP_CTRL1_SDE_EN 0x20 /* SDE enable */
|
||||
#define DSP_CTRL1_MTRX_EN 0x10 /* Color matrix ON/OFF selection */
|
||||
#define DSP_CTRL1_INTRP_EN 0x08 /* Interpolation ON/OFF selection */
|
||||
#define DSP_CTRL1_GAMMA_EN 0x04 /* Gamma function ON/OFF selection */
|
||||
#define DSP_CTRL1_BLACK_EN 0x02 /* Black defect auto correction ON/OFF */
|
||||
#define DSP_CTRL1_WHITE_EN 0x01 /* White defect auto correction ON/OFF */
|
||||
|
||||
#define DSP_CTRL2 0x65 /* DSP Control Byte 2 */
|
||||
#define DSP_CTRL2_VDCW_EN 0x08 /* Vertical DCW enable */
|
||||
#define DSP_CTRL2_HDCW_EN 0x04 /* Horizontal DCW enable */
|
||||
#define DSP_CTRL2_VZOOM_EN 0x02 /* Vertical zoom out enable */
|
||||
#define DSP_CTRL2_HZOOM_EN 0x01 /* Horizontal zoom out enable */
|
||||
|
||||
#define DSP_CTRL3 0x66 /* DSP Control Byte 3 */
|
||||
#define DSP_CTRL3_UV_EN 0x80 /* UV output sequence option */
|
||||
#define DSP_CTRL3_CBAR_EN 0x20 /* DSP color bar ON/OFF selection */
|
||||
#define DSP_CTRL3_FIFO_EN 0x08 /* FIFO power down ON/OFF selection */
|
||||
#define DSP_CTRL3_SCAL1_PWDN 0x04 /* Scaling module power down control 1 */
|
||||
#define DSP_CTRL3_SCAL2_PWDN 0x02 /* Scaling module power down control 2 */
|
||||
#define DSP_CTRL3_INTRP_PWDN 0x01 /* Interpolation module power down control */
|
||||
#define DSP_CTRL3_SET_CBAR(r, x) ((r&0xDF)|((x&1)<<5))
|
||||
|
||||
|
||||
#define DSP_CTRL4 0x67 /* DSP Control Byte 4 */
|
||||
#define DSP_CTRL4_YUV_RGB 0x00 /* Output selection YUV or RGB */
|
||||
#define DSP_CTRL4_RAW8 0x02 /* Output selection RAW8 */
|
||||
#define DSP_CTRL4_RAW10 0x03 /* Output selection RAW10 */
|
||||
|
||||
|
||||
#define AWB_BIAS 0x68 /* AWB BLC Level Clip */
|
||||
#define AWB_CTRL1 0x69 /* AWB Control 1 */
|
||||
#define AWB_CTRL2 0x6A /* AWB Control 2 */
|
||||
|
||||
#define AWB_CTRL3 0x6B /* AWB Control 3 */
|
||||
#define AWB_CTRL3_ADVANCED 0x80 /* AWB mode select - Advanced AWB */
|
||||
#define AWB_CTRL3_SIMPLE 0x00 /* AWB mode select - Simple AWB */
|
||||
|
||||
#define AWB_CTRL4 0x6C /* AWB Control 4 */
|
||||
#define AWB_CTRL5 0x6D /* AWB Control 5 */
|
||||
#define AWB_CTRL6 0x6E /* AWB Control 6 */
|
||||
#define AWB_CTRL7 0x6F /* AWB Control 7 */
|
||||
#define AWB_CTRL8 0x70 /* AWB Control 8 */
|
||||
#define AWB_CTRL9 0x71 /* AWB Control 9 */
|
||||
#define AWB_CTRL10 0x72 /* AWB Control 10 */
|
||||
#define AWB_CTRL11 0x73 /* AWB Control 11 */
|
||||
#define AWB_CTRL12 0x74 /* AWB Control 12 */
|
||||
#define AWB_CTRL13 0x75 /* AWB Control 13 */
|
||||
#define AWB_CTRL14 0x76 /* AWB Control 14 */
|
||||
#define AWB_CTRL15 0x77 /* AWB Control 15 */
|
||||
#define AWB_CTRL16 0x78 /* AWB Control 16 */
|
||||
#define AWB_CTRL17 0x79 /* AWB Control 17 */
|
||||
#define AWB_CTRL18 0x7A /* AWB Control 18 */
|
||||
#define AWB_CTRL19 0x7B /* AWB Control 19 */
|
||||
#define AWB_CTRL20 0x7C /* AWB Control 20 */
|
||||
#define AWB_CTRL21 0x7D /* AWB Control 21 */
|
||||
#define GAM1 0x7E /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */
|
||||
#define GAM2 0x7F /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */
|
||||
#define GAM3 0x80 /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */
|
||||
#define GAM4 0x81 /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */
|
||||
#define GAM5 0x82 /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */
|
||||
#define GAM6 0x83 /* Gamma Curve 6th Segment Input End Point 0x30 Output Value */
|
||||
#define GAM7 0x84 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */
|
||||
#define GAM8 0x85 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */
|
||||
#define GAM9 0x86 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */
|
||||
#define GAM10 0x87 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */
|
||||
#define GAM11 0x88 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */
|
||||
#define GAM12 0x89 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */
|
||||
#define GAM13 0x8A /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */
|
||||
#define GAM14 0x8B /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */
|
||||
#define GAM15 0x8C /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */
|
||||
#define SLOP 0x8D /* Gamma Curve Highest Segment Slope */
|
||||
#define DNSTH 0x8E /* De-noise Threshold */
|
||||
#define EDGE0 0x8F /* Edge Enhancement Strength Control */
|
||||
#define EDGE1 0x90 /* Edge Enhancement Threshold Control */
|
||||
#define DNSOFF 0x91 /* Auto De-noise Threshold Control */
|
||||
#define EDGE2 0x92 /* Edge Enhancement Strength Upper Limit */
|
||||
#define EDGE3 0x93 /* Edge Enhancement Strength Upper Limit */
|
||||
#define MTX1 0x94 /* Matrix Coefficient 1 */
|
||||
#define MTX2 0x95 /* Matrix Coefficient 2 */
|
||||
#define MTX3 0x96 /* Matrix Coefficient 3 */
|
||||
#define MTX4 0x97 /* Matrix Coefficient 4 */
|
||||
#define MTX5 0x98 /* Matrix Coefficient 5 */
|
||||
#define MTX6 0x99 /* Matrix Coefficient 6 */
|
||||
|
||||
#define MTX_CTRL 0x9A /* Matrix Control */
|
||||
#define MTX_CTRL_DBL_EN 0x80 /* Matrix double ON/OFF selection */
|
||||
|
||||
#define BRIGHTNESS 0x9B /* Brightness Control */
|
||||
#define CONTRAST 0x9C /* Contrast Gain */
|
||||
#define UVADJ0 0x9E /* Auto UV Adjust Control 0 */
|
||||
#define UVADJ1 0x9F /* Auto UV Adjust Control 1 */
|
||||
#define SCAL0 0xA0 /* DCW Ratio Control */
|
||||
#define SCAL1 0xA1 /* Horizontal Zoom Out Control */
|
||||
#define SCAL2 0xA2 /* Vertical Zoom Out Control */
|
||||
#define FIFODLYM 0xA3 /* FIFO Manual Mode Delay Control */
|
||||
#define FIFODLYA 0xA4 /* FIFO Auto Mode Delay Control */
|
||||
|
||||
#define SDE 0xA6 /* Special Digital Effect Control */
|
||||
#define SDE_NEGATIVE_EN 0x40 /* Negative image enable */
|
||||
#define SDE_GRAYSCALE_EN 0x20 /* Gray scale image enable */
|
||||
#define SDE_V_FIXED_EN 0x10 /* V fixed value enable */
|
||||
#define SDE_U_FIXED_EN 0x08 /* U fixed value enable */
|
||||
#define SDE_CONT_BRIGHT_EN 0x04 /* Contrast/Brightness enable */
|
||||
#define SDE_SATURATION_EN 0x02 /* Saturation enable */
|
||||
#define SDE_HUE_EN 0x01 /* Hue enable */
|
||||
|
||||
#define USAT 0xA7 /* U Component Saturation Gain */
|
||||
#define VSAT 0xA8 /* V Component Saturation Gain */
|
||||
#define HUECOS 0xA9 /* Cosine value <20><> 0x80 */
|
||||
#define HUESIN 0xAA /* Sine value <20><> 0x80 */
|
||||
#define SIGN_BIT 0xAB /* Sign Bit for Hue and Brightness */
|
||||
|
||||
#define DSPAUTO 0xAC /* DSP Auto Function ON/OFF Control */
|
||||
#define DSPAUTO_AWB_EN 0x80 /* AWB auto threshold control */
|
||||
#define DSPAUTO_DENOISE_EN 0x40 /* De-noise auto threshold control */
|
||||
#define DSPAUTO_EDGE_EN 0x20 /* Sharpness (edge enhancement) auto strength control */
|
||||
#define DSPAUTO_UV_EN 0x10 /* UV adjust auto slope control */
|
||||
#define DSPAUTO_SCAL0_EN 0x08 /* Auto scaling factor control (register SCAL0 (0xA0)) */
|
||||
#define DSPAUTO_SCAL1_EN 0x04 /* Auto scaling factor control (registers SCAL1 (0xA1 and SCAL2 (0xA2))*/
|
||||
#define SET_REG(reg, x) (##reg_DEFAULT|x)
|
||||
#endif //__REG_REGS_H__
|
||||
#endif
|
||||
@@ -34,6 +34,12 @@ static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
|
||||
#include "xclk.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
|
||||
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "esp32 ll_cam";
|
||||
|
||||
#define I2S_ISR_ENABLE(i) {I2S0.int_clr.i = 1;I2S0.int_ena.i = 1;}
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
#include "xclk.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
|
||||
#define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "s2 ll_cam";
|
||||
|
||||
#define I2S_ISR_ENABLE(i) {I2S0.int_clr.i = 1;I2S0.int_ena.i = 1;}
|
||||
|
||||
@@ -23,6 +23,11 @@
|
||||
#include "ll_cam.h"
|
||||
#include "cam_hal.h"
|
||||
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5)
|
||||
#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
|
||||
#define gpio_matrix_out(a,b,c,d) gpio_iomux_out(a,b,c)
|
||||
#endif
|
||||
|
||||
static const char *TAG = "s3 ll_cam";
|
||||
|
||||
static void IRAM_ATTR ll_cam_vsync_isr(void *arg)
|
||||
|
||||
@@ -30,10 +30,26 @@
|
||||
|
||||
// #define DEBUG_DETAIL_ON
|
||||
|
||||
#define USE_PWM_LEDFLASH
|
||||
|
||||
#ifdef USE_PWM_LEDFLASH
|
||||
|
||||
//// PWM für Flash-LED
|
||||
#define LEDC_TIMER LEDC_TIMER_1 // LEDC_TIMER_0
|
||||
#define LEDC_MODE LEDC_LOW_SPEED_MODE
|
||||
#define LEDC_OUTPUT_IO (4) // Define the output GPIO
|
||||
#define LEDC_CHANNEL LEDC_CHANNEL_1
|
||||
#define LEDC_DUTY_RES LEDC_TIMER_13_BIT // Set duty resolution to 13 bits
|
||||
//#define LEDC_DUTY (195) // Set duty to 50%. ((2 ** 13) - 1) * 50% = 4095
|
||||
#define LEDC_FREQUENCY (5000) // Frequency in Hertz. Set frequency at 5 kHz
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// ESP32Cam (AiThinker) PIN Map
|
||||
|
||||
#define CAM_PIN_PWDN (gpio_num_t) 32
|
||||
#define CAM_PIN_PWDN 32
|
||||
#define CAM_PIN_RESET -1 //software reset will be performed
|
||||
#define CAM_PIN_XCLK 0
|
||||
#define CAM_PIN_SIOD 26
|
||||
@@ -51,6 +67,7 @@
|
||||
#define CAM_PIN_HREF 23
|
||||
#define CAM_PIN_PCLK 22
|
||||
|
||||
|
||||
static const char *TAGCAMERACLASS = "server_part_camera";
|
||||
|
||||
static camera_config_t camera_config = {
|
||||
@@ -74,17 +91,18 @@ 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 !!!! Hängt in Version 9.2 !!!!
|
||||
.ledc_timer = LEDC_TIMER_0,
|
||||
.ledc_channel = LEDC_CHANNEL_0,
|
||||
|
||||
.pixel_format = PIXFORMAT_JPEG, //YUV422,GRAYSCALE,RGB565,JPEG
|
||||
.frame_size = FRAMESIZE_VGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
|
||||
// .frame_size = FRAMESIZE_UXGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
|
||||
.jpeg_quality = 5, //0-63 lower number means higher quality
|
||||
.jpeg_quality = 12, //0-63 lower number means higher quality
|
||||
.fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
|
||||
.fb_location = CAMERA_FB_IN_PSRAM, /*!< The location where the frame buffer will be allocated */
|
||||
// .grab_mode = CAMERA_GRAB_WHEN_EMPTY,
|
||||
.grab_mode = CAMERA_GRAB_LATEST,
|
||||
.grab_mode = CAMERA_GRAB_LATEST, // erst ab neuer esp32cam-version
|
||||
|
||||
};
|
||||
|
||||
@@ -102,29 +120,36 @@ typedef struct {
|
||||
} jpg_chunking_t;
|
||||
|
||||
|
||||
#define LEDC_LS_CH2_GPIO (4)
|
||||
#define LEDC_LS_CH2_CHANNEL LEDC_CHANNEL_2
|
||||
#define LEDC_LS_TIMER LEDC_TIMER_1
|
||||
#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE
|
||||
#define LEDC_TEST_DUTY (4000)
|
||||
void CCamera::ledc_init(void)
|
||||
{
|
||||
#ifdef USE_PWM_LEDFLASH
|
||||
|
||||
void test(){
|
||||
// Prepare and then apply the LEDC PWM timer configuration
|
||||
ledc_timer_config_t ledc_timer = { };
|
||||
|
||||
ledc_timer.speed_mode = LEDC_MODE;
|
||||
ledc_timer.timer_num = LEDC_TIMER;
|
||||
ledc_timer.duty_resolution = LEDC_DUTY_RES;
|
||||
ledc_timer.freq_hz = LEDC_FREQUENCY; // Set output frequency at 5 kHz
|
||||
ledc_timer.clk_cfg = LEDC_AUTO_CLK;
|
||||
|
||||
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
|
||||
|
||||
// Prepare and then apply the LEDC PWM channel configuration
|
||||
ledc_channel_config_t ledc_channel = { };
|
||||
|
||||
ledc_channel.channel = LEDC_LS_CH2_CHANNEL;
|
||||
ledc_channel.duty = 0;
|
||||
ledc_channel.gpio_num = FLASH_GPIO;
|
||||
ledc_channel.speed_mode = LEDC_LS_MODE;
|
||||
ledc_channel.hpoint = 0;
|
||||
ledc_channel.timer_sel = LEDC_LS_TIMER;
|
||||
ledc_channel.speed_mode = LEDC_MODE;
|
||||
ledc_channel.channel = LEDC_CHANNEL;
|
||||
ledc_channel.timer_sel = LEDC_TIMER;
|
||||
ledc_channel.intr_type = LEDC_INTR_DISABLE;
|
||||
ledc_channel.gpio_num = LEDC_OUTPUT_IO;
|
||||
ledc_channel.duty = 0; // Set duty to 0%
|
||||
ledc_channel.hpoint = 0;
|
||||
|
||||
ledc_channel_config(&ledc_channel);
|
||||
|
||||
ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, LEDC_TEST_DUTY);
|
||||
ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel);
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
};
|
||||
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){
|
||||
@@ -147,9 +172,11 @@ bool CCamera::SetBrightnessContrastSaturation(int _brightness, int _contrast, in
|
||||
_brightness = min(2, max(-2, _brightness));
|
||||
if (_contrast > -100)
|
||||
_contrast = min(2, max(-2, _contrast));
|
||||
// _saturation = min(2, max(-2, _saturation));
|
||||
if (_saturation > -100)
|
||||
_saturation = min(2, max(-2, _saturation));
|
||||
|
||||
// s->set_saturation(s, _saturation);
|
||||
if (_saturation > -100)
|
||||
s->set_saturation(s, _saturation);
|
||||
if (_contrast > -100)
|
||||
s->set_contrast(s, _contrast);
|
||||
if (_brightness > -100)
|
||||
@@ -222,6 +249,7 @@ void CCamera::SetQualitySize(int qual, framesize_t resol)
|
||||
|
||||
void CCamera::EnableAutoExposure(int flashdauer)
|
||||
{
|
||||
printf("EnableAutoExposure");
|
||||
LEDOnOff(true);
|
||||
if (flashdauer > 0)
|
||||
LightOnOff(true);
|
||||
@@ -235,6 +263,9 @@ void CCamera::EnableAutoExposure(int flashdauer)
|
||||
ESP_LOGE(TAGCAMERACLASS, "Camera Capture Failed");
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
LogFile.SwitchOnOff(true);
|
||||
LogFile.WriteToFile("Camera Capture Failed (Procedure 'EnableAutoExposure') --> Reboot"
|
||||
"Check that your camera module is working and connected properly.");
|
||||
doReboot();
|
||||
}
|
||||
esp_camera_fb_return(fb);
|
||||
@@ -285,7 +316,7 @@ esp_err_t CCamera::CaptureToBasisImage(CImageBasis *_Image, int delay)
|
||||
LightOnOff(false);
|
||||
|
||||
LogFile.SwitchOnOff(true);
|
||||
LogFile.WriteToFile("Camera is not working anymore - most propably hardware problem (instablility, ...). "
|
||||
LogFile.WriteToFile("Camera is not working anymore (CCamera::CaptureToBasisImage) - most propably hardware problem (instablility, ...). "
|
||||
"System will reboot.");
|
||||
doReboot();
|
||||
|
||||
@@ -382,6 +413,9 @@ esp_err_t CCamera::CaptureToFile(std::string nm, int delay)
|
||||
ESP_LOGE(TAGCAMERACLASS, "CaptureToFile: Camera Capture Failed");
|
||||
LEDOnOff(false);
|
||||
LightOnOff(false);
|
||||
LogFile.SwitchOnOff(true);
|
||||
LogFile.WriteToFile("Camera Capture Failed (CCamera::CaptureToFile) --> Reboot"
|
||||
"Check that your camera module is working and connected properly.");
|
||||
doReboot();
|
||||
|
||||
return ESP_FAIL;
|
||||
@@ -520,15 +554,31 @@ void CCamera::LightOnOff(bool status)
|
||||
printf("Use gpioHandler flashLigh\n");
|
||||
gpioHandler->flashLightEnable(status);
|
||||
} else {
|
||||
#ifdef USE_PWM_LEDFLASH
|
||||
if (status)
|
||||
{
|
||||
printf("Internal Flash-LED turn on with PWM %d\n", led_intensity);
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_intensity));
|
||||
// Update duty to apply the new value
|
||||
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Internal Flash-LED turn off PWM\n");
|
||||
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, 0));
|
||||
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
|
||||
}
|
||||
#else
|
||||
// Init the GPIO
|
||||
gpio_pad_select_gpio(FLASH_GPIO);
|
||||
/* Set the GPIO as a push/pull output */
|
||||
// Set the GPIO as a push/pull output
|
||||
gpio_set_direction(FLASH_GPIO, GPIO_MODE_OUTPUT);
|
||||
|
||||
if (status)
|
||||
gpio_set_level(FLASH_GPIO, 1);
|
||||
else
|
||||
gpio_set_level(FLASH_GPIO, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,18 +669,12 @@ CCamera::CCamera()
|
||||
contrast = -5;
|
||||
saturation = -5;
|
||||
isFixedExposure = false;
|
||||
|
||||
ledc_init();
|
||||
}
|
||||
|
||||
esp_err_t CCamera::InitCam()
|
||||
{
|
||||
if(CAM_PIN_PWDN != -1){
|
||||
// Init the GPIO
|
||||
gpio_pad_select_gpio(CAM_PIN_PWDN);
|
||||
/* Set the GPIO as a push/pull output */
|
||||
gpio_set_direction(CAM_PIN_PWDN, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level(CAM_PIN_PWDN, 0);
|
||||
}
|
||||
|
||||
printf("Init Camera\n");
|
||||
ActualQuality = camera_config.jpeg_quality;
|
||||
ActualResolution = camera_config.frame_size;
|
||||
@@ -642,4 +686,14 @@ esp_err_t CCamera::InitCam()
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
void CCamera::SetLEDIntensity(float _intrel)
|
||||
{
|
||||
_intrel = min(_intrel, (float) 100);
|
||||
_intrel = max(_intrel, (float) 0);
|
||||
_intrel = _intrel / 100;
|
||||
led_intensity = (int) (_intrel * 8191);
|
||||
printf("Set led_intensity to %d of 8191\n", led_intensity);
|
||||
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ class CCamera {
|
||||
int brightness, contrast, saturation;
|
||||
bool isFixedExposure;
|
||||
int waitbeforepicture_org;
|
||||
int led_intensity = 4095;
|
||||
|
||||
void ledc_init(void);
|
||||
|
||||
public:
|
||||
int image_height, image_width;
|
||||
@@ -36,6 +39,7 @@ class CCamera {
|
||||
void SetQualitySize(int qual, framesize_t resol);
|
||||
bool SetBrightnessContrastSaturation(int _brightness, int _contrast, int _saturation);
|
||||
void GetCameraParameter(httpd_req_t *req, int &qual, framesize_t &resol);
|
||||
void SetLEDIntensity(float _intrel);
|
||||
|
||||
void EnableAutoExposure(int flashdauer);
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -72,40 +72,49 @@ static const char *TAG_FILESERVER = "file_server";
|
||||
|
||||
using namespace std;
|
||||
|
||||
static esp_err_t get_tflite_file_handler(httpd_req_t *req){
|
||||
DIR *verzeichnis;
|
||||
struct dirent *files;
|
||||
esp_err_t get_tflite_file_handler(httpd_req_t *req)
|
||||
{
|
||||
// DIR *verzeichnis;
|
||||
// struct dirent *files;
|
||||
struct dirent *entry;
|
||||
// struct stat entry_stat;
|
||||
|
||||
std::string _filename, _fileext, _result = "";
|
||||
std::string _delimiter = ".";
|
||||
|
||||
std::string _filename, _fileext;
|
||||
size_t pos = 0;
|
||||
|
||||
const char verz_name[] = "/sdcard/config";
|
||||
printf("Suche TFLITE in /sdcard/config/\n");
|
||||
|
||||
verzeichnis=opendir("/sdcard/config");
|
||||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||||
httpd_resp_set_type(req, "text/plain");
|
||||
|
||||
printf("Suche TFLITE in /sdcard/config\n");
|
||||
|
||||
while((files = readdir(verzeichnis)))
|
||||
DIR *dir = opendir(verz_name);
|
||||
while ((entry = readdir(dir)) != NULL)
|
||||
{
|
||||
_filename = files->d_name;
|
||||
_fileext = _filename;
|
||||
_filename = std::string(entry->d_name);
|
||||
printf("File: %s\t", _filename.c_str());
|
||||
|
||||
while ((pos = _fileext.find(_delimiter))) {
|
||||
_fileext.erase(0, pos + _delimiter.length());
|
||||
// ignore all files with starting dot (hidden files)
|
||||
if (_filename.rfind(".", 0) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
_fileext = _filename;
|
||||
pos = _fileext.find_last_of(".");
|
||||
if (pos != std::string::npos)
|
||||
_fileext = _fileext.erase(0, pos + 1);
|
||||
|
||||
printf(" Extension: %s\n", _fileext.c_str());
|
||||
|
||||
if ((_fileext == "tfl") || (_fileext == "tflite"))
|
||||
{
|
||||
_result = _result + _filename + "\t";
|
||||
_filename = "/config/" + _filename + "\t";
|
||||
httpd_resp_sendstr_chunk(req, _filename.c_str());
|
||||
}
|
||||
}
|
||||
closedir(verzeichnis);
|
||||
closedir(dir);
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -363,6 +372,7 @@ static esp_err_t download_get_handler(httpd_req_t *req)
|
||||
}
|
||||
}
|
||||
|
||||
printf("uri: %s, filename: %s, filepath: %s\n", req->uri, filename, filepath);
|
||||
return http_resp_dir_html(req, filepath, filename, readonly);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,4 +5,6 @@ void register_server_file_uri(httpd_handle_t server, const char *base_path);
|
||||
|
||||
void unzip(std::string _in_zip_file, std::string _target_directory);
|
||||
|
||||
void delete_all_in_directory(std::string _directory);
|
||||
void delete_all_in_directory(std::string _directory);
|
||||
|
||||
esp_err_t get_tflite_file_handler(httpd_req_t *req);
|
||||
|
||||
@@ -416,6 +416,8 @@ void task_reboot(void *pvParameter)
|
||||
}
|
||||
|
||||
void doReboot(){
|
||||
LogFile.SwitchOnOff(true);
|
||||
LogFile.WriteToFile("Reboot triggert by Software (5s).");
|
||||
ESP_LOGI(TAGPARTOTA, "Reboot in 5sec");
|
||||
LogFile.WriteToFile("Reboot in 5sec");
|
||||
xTaskCreate(&task_reboot, "reboot", configMINIMAL_STACK_SIZE * 64, NULL, 10, NULL);
|
||||
@@ -435,7 +437,7 @@ esp_err_t handler_reboot(httpd_req_t *req)
|
||||
|
||||
LogFile.WriteToFile("handler_reboot");
|
||||
ESP_LOGI(TAGPARTOTA, "!!! System will restart within 5 sec!!!");
|
||||
const char* resp_str = "!!! System will restart within 5 sec!!!";
|
||||
const char* resp_str = "<body style='font-family: arial'> <h3 id=t></h3></body><script>var h='Rebooting!<br>The page will automatically reload.<br>'; document.getElementById('t').innerHTML=h; setInterval(function (){h +='.'; document.getElementById('t').innerHTML=h; fetch(window.location.hostname,{mode: 'no-cors'}).then(r=>{window.location.replace('/wasserzaehler_roi.html');})}, 1000);</script>";
|
||||
httpd_resp_send(req, resp_str, strlen(resp_str));
|
||||
|
||||
doReboot();
|
||||
|
||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
|
||||
REQUIRES jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ void ClassFlow::SetInitialParameter(void)
|
||||
std::vector<string> ClassFlow::ZerlegeZeile(std::string input, std::string delimiter)
|
||||
{
|
||||
std::vector<string> Output;
|
||||
// std::string delimiter = " =,";
|
||||
|
||||
input = trim(input, delimiter);
|
||||
size_t pos = findDelimiterPos(input, delimiter);
|
||||
|
||||
@@ -26,7 +26,6 @@ struct HTMLInfo
|
||||
class ClassFlow
|
||||
{
|
||||
protected:
|
||||
// std::vector<string> ZerlegeZeile(string input);
|
||||
std::vector<string> ZerlegeZeile(string input, string delimiter = " =, \t");
|
||||
bool isNewParagraph(string input);
|
||||
bool GetNextParagraph(FILE* pfile, string& aktparamgraph);
|
||||
|
||||
@@ -19,6 +19,7 @@ void ClassFlowAlignment::SetInitialParameter(void)
|
||||
initalrotate = 0;
|
||||
anz_ref = 0;
|
||||
initialmirror = false;
|
||||
use_antialiasing = false;
|
||||
initialflip = false;
|
||||
SaveAllFiles = false;
|
||||
namerawimage = "/sdcard/img_tmp/raw.jpg";
|
||||
@@ -94,7 +95,12 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
if ((toUpper(zerlegt[0]) == "SEARCHFIELDY") && (zerlegt.size() > 1))
|
||||
{
|
||||
suchey = std::stod(zerlegt[1]);
|
||||
}
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "ANTIALIASING") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
use_antialiasing = true;
|
||||
}
|
||||
if ((zerlegt.size() == 3) && (anz_ref < 2))
|
||||
{
|
||||
References[anz_ref].image_file = FormatFileName("/sdcard" + zerlegt[0]);
|
||||
@@ -175,7 +181,10 @@ bool ClassFlowAlignment::doFlow(string time)
|
||||
|
||||
if ((initalrotate != 0) || initialflip)
|
||||
{
|
||||
rt.Rotate(initalrotate);
|
||||
if (use_antialiasing)
|
||||
rt.RotateAntiAliasing(initalrotate);
|
||||
else
|
||||
rt.Rotate(initalrotate);
|
||||
if (SaveAllFiles) AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ protected:
|
||||
float initalrotate;
|
||||
bool initialmirror;
|
||||
bool initialflip;
|
||||
bool use_antialiasing;
|
||||
RefInfo References[2];
|
||||
int anz_ref;
|
||||
string namerawimage;
|
||||
|
||||
@@ -17,40 +17,32 @@ ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNTy
|
||||
string cnnmodelfile = "";
|
||||
modelxsize = 1;
|
||||
modelysize = 1;
|
||||
CNNGoodThreshold = 0.0;
|
||||
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 ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution, int prev, float _vorgaengerAnalog)
|
||||
{
|
||||
string result = "";
|
||||
|
||||
if (GENERAL[_analog]->ROI.size() == 0)
|
||||
return result;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout _analog=" + std::to_string(_analog) + ", _extendedResolution=" + std::to_string(_extendedResolution) + ", prev=" + std::to_string(prev));
|
||||
|
||||
if (CNNType == Analogue)
|
||||
if (CNNType == Analogue || CNNType == Analogue100)
|
||||
{
|
||||
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);
|
||||
|
||||
prev = ZeigerEvalAnalogNeu(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev);
|
||||
// if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout(analog) zahl=" + std::to_string(zahl) + ", ergebnis_nachkomma=" + std::to_string(ergebnis_nachkomma) + ", prev=" + std::to_string(prev));
|
||||
result = std::to_string(prev);
|
||||
|
||||
if (_extendedResolution && (CNNType != Digital))
|
||||
@@ -58,7 +50,7 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
|
||||
for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
|
||||
{
|
||||
prev = ZeigerEval(GENERAL[_analog]->ROI[i]->result_float, prev);
|
||||
prev = ZeigerEvalAnalogNeu(GENERAL[_analog]->ROI[i]->result_float, prev);
|
||||
result = std::to_string(prev) + result;
|
||||
}
|
||||
return result;
|
||||
@@ -76,26 +68,31 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
return result;
|
||||
}
|
||||
|
||||
if (CNNType == DigitalHyprid)
|
||||
if ((CNNType == DoubleHyprid10) || (CNNType == Digital100))
|
||||
{
|
||||
// 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)
|
||||
if (_extendedResolution) // ist nur gesetzt, falls es die erste Ziffer ist (kein Analog vorher!)
|
||||
{
|
||||
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;
|
||||
prev = ergebnis_vorkomma;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout(dig100-ext) ergebnis_vorkomma=" + std::to_string(ergebnis_vorkomma) + ", ergebnis_nachkomma=" + std::to_string(ergebnis_nachkomma) + ", prev=" + std::to_string(prev));
|
||||
}
|
||||
else
|
||||
{
|
||||
zif_akt = ZeigerEvalHybrid(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, -1, -1);
|
||||
result = std::to_string(zif_akt);
|
||||
// prev = ZeigerEval(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev);
|
||||
if (_vorgaengerAnalog >= 0)
|
||||
prev = ZeigerEvalHybridNeu(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, _vorgaengerAnalog, prev, true);
|
||||
else
|
||||
prev = ZeigerEvalHybridNeu(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev, prev);
|
||||
result = std::to_string(prev);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout(dig100) prev=" + std::to_string(prev));
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -109,27 +106,37 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
|
||||
{
|
||||
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;
|
||||
prev = ZeigerEvalHybridNeu(GENERAL[_analog]->ROI[i]->result_float, GENERAL[_analog]->ROI[i+1]->result_float, prev);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout#ZeigerEvalHybridNeu()= " + std::to_string(prev));
|
||||
result = std::to_string(prev) + result;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout#result= " + result);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
zif_akt = -1;
|
||||
prev = -1;
|
||||
result = "N" + result;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout(result_float<0 /'N') result_float=" + std::to_string(GENERAL[_analog]->ROI[i]->result_float));
|
||||
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger)
|
||||
{
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalHybrid( " + std::to_string(zahl) + ", " + std::to_string(zahl_vorgaenger) + ", " + std::to_string(eval_vorgaenger) + ")");
|
||||
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
// int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
|
||||
int ergebnis_vorkomma = ((int) floor(zahl) + 10) % 10;
|
||||
|
||||
if (zahl_vorgaenger < 0) // keine Vorzahl vorhanden !!! --> Runde die Zahl
|
||||
|
||||
if (eval_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;
|
||||
@@ -137,46 +144,221 @@ int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int
|
||||
return ((int) trunc(zahl) + 10) % 10;
|
||||
}
|
||||
|
||||
if (zahl_vorgaenger > 9.2) // Ziffernwechsel beginnt
|
||||
// 9.0, da bei getReadout() prev als int übergeben wird (9 statt 9.5)
|
||||
// tritt bei der ersten ziffer von digit auf, wenn analog davor (2. Aufruf von getReadout)
|
||||
if ((zahl_vorgaenger >= 0.5 ) && (zahl_vorgaenger < 9.5))
|
||||
{
|
||||
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
|
||||
}
|
||||
// kein Ziffernwechsel, da Vorkomma weit genug weg ist (0+/-0.5) --> zahl wird gerundet
|
||||
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
|
||||
{
|
||||
if (eval_vorgaenger <= 1) // Nulldurchgang hat stattgefunden (!Bewertung über Prev_value und nicht Zahl!) --> hier aufrunden (2.8 --> 3, aber auch 3.1 --> 3)
|
||||
{
|
||||
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;
|
||||
}
|
||||
if (ergebnis_nachkomma > 5)
|
||||
return (ergebnis_vorkomma + 1) % 10;
|
||||
else
|
||||
{
|
||||
return ((int) trunc(zahl) + 10) % 10; // Wechsel schon weiter fortgeschritten, d.h. über 2 als Nachkomma
|
||||
}
|
||||
return ergebnis_vorkomma;
|
||||
}
|
||||
else // bleibt nur >= 9.5 --> noch kein Nulldurchgang --> 2.8 --> 2, und 3.1 --> 2
|
||||
{
|
||||
// hier auf 4 reduziert, da erst ab Vorgänder 9 anfängt umzustellen. Bei 9.5 Vorgänger kann die aktuelle
|
||||
// Zahl noch x.4 - x.5 sein.
|
||||
if (ergebnis_nachkomma >= 4)
|
||||
return ergebnis_vorkomma;
|
||||
else
|
||||
return (ergebnis_vorkomma - 1 + 10) % 10;
|
||||
}
|
||||
}
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalHybrid(return -1) zahl=" + std::to_string(zahl)
|
||||
+ ", zahl_vorgaenger=" + std::to_string(zahl_vorgaenger) + ", eval_vorgaenger=" + std::to_string(eval_vorgaenger));
|
||||
return -1;
|
||||
|
||||
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::ZeigerEvalHybridNeu(float zahl, float zahl_vorgaenger, int eval_vorgaenger, bool AnalogerVorgaenger)
|
||||
{
|
||||
int result;
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
int ergebnis_vorkomma = ((int) floor(zahl) + 10) % 10;
|
||||
|
||||
if (eval_vorgaenger < 0)
|
||||
{
|
||||
if ((ergebnis_nachkomma <= DigitalUnschaerfe * 10) || (ergebnis_nachkomma >= DigitalUnschaerfe * 10)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||
result = (int) (round(zahl) + 10) % 10;
|
||||
else
|
||||
result = (int) ((int) trunc(zahl) + 10) % 10;
|
||||
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalHybridNeu - kein Vorgänger - Ergebnis = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " zahl_vorgaenger = " + std::to_string(zahl_vorgaenger)+ " eval_vorgaenger = " + std::to_string(eval_vorgaenger) + " DigitalUnschaerfe = " + std::to_string(DigitalUnschaerfe));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (AnalogerVorgaenger)
|
||||
{
|
||||
// result = ZeigerEvalAnalogToDigitNeu(zahl, eval_vorgaenger);
|
||||
result = ZeigerEvalAnalogToDigitNeu(zahl, zahl_vorgaenger);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalHybridNeu - Analoger Vorgänger, Bewertung über ZeigerEvalAnalogNeu = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " zahl_vorgaenger = " + std::to_string(zahl_vorgaenger)+ " eval_vorgaenger = " + std::to_string(eval_vorgaenger) + " DigitalUnschaerfe = " + std::to_string(DigitalUnschaerfe));
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((zahl_vorgaenger >= DigitalUebergangsbereichVorgaenger ) && (zahl_vorgaenger <= (10.0 - DigitalUebergangsbereichVorgaenger)))
|
||||
{
|
||||
// kein Ziffernwechsel, da Vorgänger weit genug weg ist (0+/-DigitalUebergangsbereichVorgaenger) --> zahl wird gerundet
|
||||
if ((ergebnis_nachkomma <= DigitalBand) || (ergebnis_nachkomma >= (10-DigitalBand))) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||
result = ((int) round(zahl) + 10) % 10;
|
||||
else
|
||||
result = ((int) trunc(zahl) + 10) % 10;
|
||||
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalHybridNeu - KEIN Analoger Vorgänger, kein Ziffernwechsel, da Vorkomma weit genug weg = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " zahl_vorgaenger = " + std::to_string(zahl_vorgaenger)+ " eval_vorgaenger = " + std::to_string(eval_vorgaenger) + " DigitalUnschaerfe = " + std::to_string(DigitalUnschaerfe));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (eval_vorgaenger <= 1) // Nulldurchgang hat stattgefunden (!Bewertung über Prev_value und nicht Zahl!) --> hier aufrunden (2.8 --> 3, aber auch 3.1 --> 3)
|
||||
{
|
||||
if (ergebnis_nachkomma > 5)
|
||||
result = (ergebnis_vorkomma + 1) % 10;
|
||||
else
|
||||
result = ergebnis_vorkomma;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalHybridNeu - KEIN Analoger Vorgänger, Nulldurchgang hat stattgefunden = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " zahl_vorgaenger = " + std::to_string(zahl_vorgaenger)+ " eval_vorgaenger = " + std::to_string(eval_vorgaenger) + " DigitalUnschaerfe = " + std::to_string(DigitalUnschaerfe));
|
||||
return result;
|
||||
}
|
||||
|
||||
// bleibt nur >= 9.5 --> noch kein Nulldurchgang --> 2.8 --> 2, und 3.1 --> 2
|
||||
// hier auf 4 reduziert, da erst ab Vorgänder 9 anfängt umzustellen. Bei 9.5 Vorgänger kann die aktuelle
|
||||
// Zahl noch x.4 - x.5 sein.
|
||||
if (ergebnis_nachkomma >= 4)
|
||||
result = ergebnis_vorkomma;
|
||||
else
|
||||
result = (ergebnis_vorkomma - 1 + 10) % 10;
|
||||
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalHybridNeu - KEIN Analoger Vorgänger, >= 9.5 --> noch kein Nulldurchgang = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " zahl_vorgaenger = " + std::to_string(zahl_vorgaenger)+ " eval_vorgaenger = " + std::to_string(eval_vorgaenger) + " DigitalUnschaerfe = " + std::to_string(DigitalUnschaerfe));
|
||||
return result;
|
||||
}
|
||||
|
||||
int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
|
||||
|
||||
int ClassFlowCNNGeneral::ZeigerEvalAnalogToDigitNeu(float zahl, float ziffer_vorgaenger)
|
||||
{
|
||||
int result;
|
||||
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
|
||||
int ergebnis_vorkomma = ((int) floor(zahl) + 10) % 10;
|
||||
|
||||
if (ziffer_vorgaenger < 0)
|
||||
{
|
||||
result = (int) floor(zahl);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalAnalogToDigitNeu - kein Vorgänger - Ergebnis = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " ziffer_vorgaenger = " + std::to_string(ziffer_vorgaenger) + " AnalogFehler = " + std::to_string(AnalogFehler));
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((ziffer_vorgaenger >= DigitalUebergangsbereichVorgaengerAnalogToDigit ) && (ziffer_vorgaenger <= (10.0 - DigitalUebergangsbereichVorgaengerAnalogToDigit)))
|
||||
{
|
||||
// kein Ziffernwechsel, da Vorgänger weit genug weg ist (0+/-DigitalUebergangsbereichVorgaenger) --> zahl wird gerundet
|
||||
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
|
||||
result = ((int) round(zahl) + 10) % 10;
|
||||
else
|
||||
result = ((int) trunc(zahl) + 10) % 10;
|
||||
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalAnalogToDigitNeu - kein Ziffernwechsel, da Vorkomma weit genug weg = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " ziffer_vorgaenger = " + std::to_string(ziffer_vorgaenger) + " DigitalUnschaerfe = " + std::to_string(DigitalUnschaerfe));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ziffer_vorgaenger <= 1) // Nulldurchgang hat stattgefunden (!Bewertung über Prev_value und nicht Zahl!) --> hier aufrunden (2.8 --> 3, aber auch 3.1 --> 3)
|
||||
{
|
||||
if (ergebnis_nachkomma > 5)
|
||||
result = (ergebnis_vorkomma + 1) % 10;
|
||||
else
|
||||
result = ergebnis_vorkomma;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalAnalogToDigitNeu - Nulldurchgang hat stattgefunden = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " ziffer_vorgaenger = " + std::to_string(ziffer_vorgaenger) + " DigitalUnschaerfe = " + std::to_string(DigitalUnschaerfe));
|
||||
return result;
|
||||
}
|
||||
|
||||
// bleibt nur >= 9.5 --> noch kein Nulldurchgang --> 2.8 --> 2, und 3.1 --> 2
|
||||
// hier auf 4 reduziert, da erst ab Vorgänder 9 anfängt umzustellen. Bei 9.5 Vorgänger kann die aktuelle
|
||||
// Zahl noch x.4 - x.5 sein.
|
||||
if (ergebnis_nachkomma >= 4)
|
||||
result = ergebnis_vorkomma;
|
||||
else
|
||||
result = (ergebnis_vorkomma - 1 + 10) % 10;
|
||||
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalAnalogToDigitNeu - 9.0 --> noch kein Nulldurchgang = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " ziffer_vorgaenger = " + std::to_string(ziffer_vorgaenger) + " DigitalUnschaerfe = " + std::to_string(DigitalUnschaerfe));
|
||||
return result;
|
||||
}
|
||||
|
||||
int ClassFlowCNNGeneral::ZeigerEvalAnalogNeu(float zahl, int ziffer_vorgaenger)
|
||||
{
|
||||
float zahl_min, zahl_max;
|
||||
int result;
|
||||
|
||||
if (ziffer_vorgaenger == -1)
|
||||
{
|
||||
result = (int) floor(zahl);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalAnalogNeu - kein Vorgänger - Ergebnis = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " ziffer_vorgaenger = " + std::to_string(ziffer_vorgaenger) + " AnalogFehler = " + std::to_string(AnalogFehler));
|
||||
return result;
|
||||
}
|
||||
|
||||
zahl_min = zahl - AnalogFehler / 10.0;
|
||||
zahl_max = zahl + AnalogFehler / 10.0;
|
||||
|
||||
if ((int) floor(zahl_max) - (int) floor(zahl_min) != 0)
|
||||
{
|
||||
if (ziffer_vorgaenger <= AnalogFehler)
|
||||
{
|
||||
result = ((int) floor(zahl_max) + 10) % 10;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalAnalogNeu - Zahl uneindeutig, Korrektur nach oben - Ergebnis = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " ziffer_vorgaenger = " + std::to_string(ziffer_vorgaenger) + " AnalogFehler = " + std::to_string(AnalogFehler));
|
||||
return result;
|
||||
}
|
||||
if (ziffer_vorgaenger >= 10 - AnalogFehler)
|
||||
{
|
||||
result = ((int) floor(zahl_min) + 10) % 10;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalAnalogNeu - Zahl uneindeutig, Korrektur nach unten - Ergebnis = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " ziffer_vorgaenger = " + std::to_string(ziffer_vorgaenger) + " AnalogFehler = " + std::to_string(AnalogFehler));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
result = ((int) floor(zahl) + 10) % 10;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEvalAnalogNeu - Zahl eindeutig, keine Korrektur notwendig - Ergebnis = " + std::to_string(result) +
|
||||
" zahl: " + std::to_string(zahl) + " ziffer_vorgaenger = " + std::to_string(ziffer_vorgaenger) + " AnalogFehler = " + std::to_string(AnalogFehler));
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
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;
|
||||
int ergebnis;
|
||||
float ergebnis_rating;
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEval erg_v=" + std::to_string(ergebnis_vorkomma) + ", erg_n=" + std::to_string(ergebnis_nachkomma) + ", ziff_v=" + std::to_string(ziffer_vorgaenger));
|
||||
|
||||
if (ziffer_vorgaenger == -1)
|
||||
return ergebnis_vorkomma % 10;
|
||||
|
||||
// Ist die aktuelle Stelle schon umgesprungen und die Vorstelle noch nicht?
|
||||
// Akt.: 2.1, Vorstelle = 0.9 => 1.9
|
||||
// Problem sind mehrere Rundungen
|
||||
// Bsp. zahl=4.5, Vorgänger= 9.6 (ziffer_vorgaenger=0)
|
||||
// Tritt nur auf bei Übergang von analog auf digit
|
||||
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
|
||||
if (ergebnis_nachkomma >= 5)
|
||||
ergebnis_rating-=5;
|
||||
ergebnis_rating-=5.1;
|
||||
else
|
||||
ergebnis_rating+=5;
|
||||
ergebnis = (int) round(zahl);
|
||||
@@ -184,10 +366,11 @@ int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
|
||||
ergebnis-=1;
|
||||
if (ergebnis == -1)
|
||||
ergebnis+=10;
|
||||
|
||||
|
||||
ergebnis = (ergebnis + 10) % 10;
|
||||
return ergebnis;
|
||||
}
|
||||
*/
|
||||
|
||||
bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
@@ -206,13 +389,6 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
) // 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;
|
||||
@@ -225,12 +401,12 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||
if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
|
||||
if ((toUpper(zerlegt[0]) == "LOGIMAGELOCATION") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->LogImageLocation = "/sdcard" + zerlegt[1];
|
||||
this->isLogImage = true;
|
||||
}
|
||||
if ((zerlegt[0] == "LogImageSelect") && (zerlegt.size() > 1))
|
||||
if ((toUpper(zerlegt[0]) == "LOGIMAGESELECT") && (zerlegt.size() > 1))
|
||||
{
|
||||
LogImageSelect = zerlegt[1];
|
||||
isLogImageSelect = true;
|
||||
@@ -240,20 +416,20 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
this->logfileRetentionInDays = std::stoi(zerlegt[1]);
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "MODELTYPE") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "DIGITHYPRID")
|
||||
CNNType = DigitalHyprid;
|
||||
}
|
||||
// if ((toUpper(zerlegt[0]) == "MODELTYPE") && (zerlegt.size() > 1))
|
||||
// {
|
||||
// if (toUpper(zerlegt[1]) == "DIGITHYPRID")
|
||||
// CNNType = DigitalHyprid;
|
||||
// }
|
||||
|
||||
if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
|
||||
if ((toUpper(zerlegt[0]) == "MODEL") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->cnnmodelfile = zerlegt[1];
|
||||
}
|
||||
if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "CNNGOODTHRESHOLD") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->modelxsize = std::stoi(zerlegt[1]);
|
||||
this->modelysize = std::stoi(zerlegt[2]);
|
||||
CNNGoodThreshold = std::stof(zerlegt[1]);
|
||||
}
|
||||
if (zerlegt.size() >= 5)
|
||||
{
|
||||
@@ -273,21 +449,16 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
SaveAllFiles = true;
|
||||
}
|
||||
|
||||
/*
|
||||
if ((toUpper(zerlegt[0]) == "EXTENDEDRESOLUTION") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
extendedResolution = true;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if (!getNetworkParameter())
|
||||
return false;
|
||||
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
|
||||
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 = new CImageBasis(modelxsize, modelysize, modelchannel);
|
||||
GENERAL[_ana]->ROI[i]->image_org = new CImageBasis(GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3);
|
||||
}
|
||||
|
||||
@@ -303,7 +474,6 @@ general* ClassFlowCNNGeneral::FindGENERAL(string _name_number)
|
||||
}
|
||||
|
||||
|
||||
|
||||
general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true)
|
||||
{
|
||||
string _analog, _roi;
|
||||
@@ -338,6 +508,7 @@ general* ClassFlowCNNGeneral::GetGENERAL(string _name, bool _create = true)
|
||||
|
||||
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());
|
||||
@@ -425,7 +596,7 @@ bool ClassFlowCNNGeneral::doAlignAndCut(string time)
|
||||
|
||||
void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
|
||||
{
|
||||
if (CNNType == Analogue)
|
||||
if (CNNType == Analogue || CNNType == Analogue100)
|
||||
{
|
||||
int r = 0;
|
||||
int g = 255;
|
||||
@@ -435,7 +606,7 @@ void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
|
||||
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->drawEllipse( (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), (int) (GENERAL[_ana]->ROI[i]->deltay/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);
|
||||
}
|
||||
@@ -448,6 +619,72 @@ void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::getNetworkParameter()
|
||||
{
|
||||
if (disabled)
|
||||
return true;
|
||||
|
||||
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)
|
||||
{
|
||||
tflite->GetInputDimension(false);
|
||||
modelxsize = tflite->ReadInputDimenstion(0);
|
||||
modelysize = tflite->ReadInputDimenstion(1);
|
||||
modelchannel = tflite->ReadInputDimenstion(2);
|
||||
|
||||
int _anzoutputdimensions = tflite->GetAnzOutPut();
|
||||
switch (_anzoutputdimensions)
|
||||
{
|
||||
case 2:
|
||||
CNNType = Analogue;
|
||||
printf("TFlite-Type set to Analogue\n");
|
||||
break;
|
||||
case 10:
|
||||
CNNType = DoubleHyprid10;
|
||||
printf("TFlite-Type set to DoubleHyprid10\n");
|
||||
break;
|
||||
case 11:
|
||||
CNNType = Digital;
|
||||
printf("TFlite-Type set to Digital\n");
|
||||
break;
|
||||
case 20:
|
||||
CNNType = DigitalHyprid10;
|
||||
printf("TFlite-Type set to DigitalHyprid10\n");
|
||||
break;
|
||||
// case 22:
|
||||
// CNNType = DigitalHyprid;
|
||||
// printf("TFlite-Type set to DigitalHyprid\n");
|
||||
// break;
|
||||
case 100:
|
||||
if (modelxsize==32 && modelysize == 32) {
|
||||
CNNType = Analogue100;
|
||||
printf("TFlite-Type set to Analogue100\n");
|
||||
} else {
|
||||
CNNType = Digital100;
|
||||
printf("TFlite-Type set to Digital\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogFile.WriteToFile("ERROR ERROR ERROR - tflite passt nicht zur Firmware - ERROR ERROR ERROR (outout_dimension=" + std::to_string(_anzoutputdimensions) + ")");
|
||||
printf("ERROR ERROR ERROR - tflite passt nicht zur Firmware - ERROR ERROR ERROR\n");
|
||||
}
|
||||
}
|
||||
|
||||
delete tflite;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
{
|
||||
if (disabled)
|
||||
@@ -468,29 +705,6 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
}
|
||||
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)
|
||||
@@ -515,6 +729,7 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
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;
|
||||
@@ -523,17 +738,19 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
|
||||
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);
|
||||
LogImage(logPath, _imagename, 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);
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
/*
|
||||
case DigitalHyprid:
|
||||
{
|
||||
int _num, _nachkomma;
|
||||
@@ -559,8 +776,172 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
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);
|
||||
{
|
||||
string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
*/
|
||||
case DigitalHyprid10:
|
||||
{
|
||||
int _num, _nachkomma;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
|
||||
|
||||
_num = tflite->GetOutClassification(0, 9);
|
||||
_nachkomma = tflite->GetOutClassification(10, 19);
|
||||
|
||||
|
||||
string _zwres = "Nach Invoke - Nummer: " + to_string(_num) + " Nachkomma: " + to_string(_nachkomma);
|
||||
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
|
||||
|
||||
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)
|
||||
{
|
||||
string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case DoubleHyprid10:
|
||||
{
|
||||
int _num, _numplus, _numminus;
|
||||
float _val, _valplus, _valminus;
|
||||
float _fit;
|
||||
float _result_save_file;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
|
||||
|
||||
_num = tflite->GetOutClassification(0, 9);
|
||||
_numplus = (_num + 1) % 10;
|
||||
_numminus = (_num - 1 + 10) % 10;
|
||||
|
||||
_val = tflite->GetOutputValue(_num);
|
||||
_valplus = tflite->GetOutputValue(_numplus);
|
||||
_valminus = tflite->GetOutputValue(_numminus);
|
||||
|
||||
float result = _num;
|
||||
|
||||
if (_valplus > _valminus)
|
||||
{
|
||||
result = result + _valplus / (_valplus + _val);
|
||||
_fit = _val + _valplus;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = result - _valminus / (_val + _valminus);
|
||||
_fit = _val + _valminus;
|
||||
|
||||
}
|
||||
if (result > 10)
|
||||
result = result - 10;
|
||||
if (result < 0)
|
||||
result = result + 10;
|
||||
|
||||
string zw = "_num (p, m): " + to_string(_num) + " " + to_string(_numplus) + " " + to_string(_numminus);
|
||||
zw = zw + " _val (p, m): " + to_string(_val) + " " + to_string(_valplus) + " " + to_string(_valminus);
|
||||
zw = zw + " result: " + to_string(result) + " _fit: " + to_string(_fit);
|
||||
printf("details cnn: %s\n", zw.c_str());
|
||||
LogFile.WriteToFile(zw);
|
||||
|
||||
|
||||
_result_save_file = result;
|
||||
|
||||
if (_fit < CNNGoodThreshold)
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->isReject = true;
|
||||
result = -1;
|
||||
_result_save_file+= 100; // Für den Fall, dass fit nicht ausreichend, soll trotzdem das Ergebnis mit "-10x.y" abgespeichert werden.
|
||||
string zw = "Value Rejected due to Threshold (Fit: " + to_string(_fit) + "Threshold: " + to_string(CNNGoodThreshold);
|
||||
printf("Value Rejected due to Threshold (Fit: %f, Threshold: %f\n", _fit, CNNGoodThreshold);
|
||||
LogFile.WriteToFile(zw);
|
||||
}
|
||||
else
|
||||
{
|
||||
GENERAL[_ana]->ROI[i]->isReject = false;
|
||||
}
|
||||
|
||||
|
||||
GENERAL[_ana]->ROI[i]->result_float = result;
|
||||
printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Digital100:
|
||||
case Analogue100:
|
||||
{
|
||||
int _num;
|
||||
float _result_save_file;
|
||||
|
||||
tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
|
||||
tflite->Invoke();
|
||||
|
||||
_num = tflite->GetOutClassification();
|
||||
|
||||
GENERAL[_ana]->ROI[i]->result_float = (float)_num / 10.0;
|
||||
|
||||
|
||||
_result_save_file = GENERAL[_ana]->ROI[i]->result_float;
|
||||
|
||||
|
||||
GENERAL[_ana]->ROI[i]->isReject = false;
|
||||
|
||||
printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
|
||||
|
||||
if (isLogImage)
|
||||
{
|
||||
string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
|
||||
if (isLogImageSelect)
|
||||
{
|
||||
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -574,7 +955,6 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
|
||||
|
||||
bool ClassFlowCNNGeneral::isExtendedResolution(int _number)
|
||||
{
|
||||
// if (extendedResolution && !(CNNType == Digital))
|
||||
if (!(CNNType == Digital))
|
||||
return true;
|
||||
|
||||
@@ -590,11 +970,14 @@ std::vector<HTMLInfo*> ClassFlowCNNGeneral::GetHTMLInfo()
|
||||
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
|
||||
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
|
||||
{
|
||||
printf("Image: %d\n", (int) GENERAL[_ana]->ROI[i]->image);
|
||||
if (GENERAL[_ana]->ROI[i]->image)
|
||||
{
|
||||
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")
|
||||
@@ -615,13 +998,9 @@ std::vector<HTMLInfo*> ClassFlowCNNGeneral::GetHTMLInfo()
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,17 @@
|
||||
|
||||
#include"ClassFlowDefineTypes.h"
|
||||
#include "ClassFlowAlignment.h"
|
||||
// #include "ClassFlowPostProcessing.h"
|
||||
|
||||
|
||||
enum t_CNNType {
|
||||
AutoDetect,
|
||||
Analogue,
|
||||
Analogue100,
|
||||
Digital,
|
||||
DigitalHyprid,
|
||||
// DigitalHyprid,
|
||||
DigitalHyprid10,
|
||||
DoubleHyprid10,
|
||||
Digital100,
|
||||
None
|
||||
};
|
||||
|
||||
@@ -20,9 +23,17 @@ class ClassFlowCNNGeneral :
|
||||
protected:
|
||||
t_CNNType CNNType;
|
||||
std::vector<general*> GENERAL;
|
||||
float CNNGoodThreshold;
|
||||
float AnalogFehler = 3.0;
|
||||
float AnalogToDigtalFehler = 0.8;
|
||||
float DigitalUnschaerfe = 0.2;
|
||||
int DigitalBand = 3;
|
||||
float DigitalAnalogerVorgaengerUebergangsbereich = 2;
|
||||
float DigitalUebergangsbereichVorgaengerAnalogToDigit = 1; // war vorher 2
|
||||
float DigitalUebergangsbereichVorgaenger = 0.9;
|
||||
|
||||
string cnnmodelfile;
|
||||
int modelxsize, modelysize;
|
||||
int modelxsize, modelysize, modelchannel;
|
||||
bool isLogImageSelect;
|
||||
string LogImageSelect;
|
||||
ClassFlowAlignment* flowpostalignment;
|
||||
@@ -30,13 +41,19 @@ protected:
|
||||
bool SaveAllFiles;
|
||||
// bool extendedResolution;
|
||||
|
||||
int ZeigerEval(float zahl, int ziffer_vorgaenger);
|
||||
int ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger);
|
||||
// int ZeigerEval(float zahl, int ziffer_vorgaenger);
|
||||
// int ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger);
|
||||
int ZeigerEvalAnalogNeu(float zahl, int ziffer_vorgaenger);
|
||||
int ZeigerEvalAnalogToDigitNeu(float zahl, float ziffer_vorgaenger);
|
||||
int ZeigerEvalHybridNeu(float zahl, float zahl_vorgaenger, int eval_vorgaenger, bool AnalogerVorgaenger = false);
|
||||
|
||||
|
||||
|
||||
bool doNeuralNetwork(string time);
|
||||
bool doAlignAndCut(string time);
|
||||
|
||||
bool getNetworkParameter();
|
||||
|
||||
public:
|
||||
ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype = AutoDetect);
|
||||
|
||||
@@ -44,13 +61,12 @@ public:
|
||||
bool doFlow(string time);
|
||||
|
||||
string getHTMLSingleStep(string host);
|
||||
string getReadout(int _analog, bool _extendedResolution);
|
||||
string getReadout(int _analog, bool _extendedResolution = false, int prev = -1, float _vorgaengerAnalog = -1);
|
||||
|
||||
void DrawROI(CImageBasis *_zw);
|
||||
|
||||
std::vector<HTMLInfo*> GetHTMLInfo();
|
||||
|
||||
// int AnzahlROIs(int _analog);
|
||||
int getAnzahlGENERAL();
|
||||
general* GetGENERAL(int _analog);
|
||||
general* GetGENERAL(string _name, bool _create);
|
||||
@@ -59,8 +75,6 @@ public:
|
||||
|
||||
bool isExtendedResolution(int _number = 0);
|
||||
|
||||
// void setPostprocessing(ClassFlowPostProcessing *_fpp){flowpostprocessing = _fpp;};
|
||||
|
||||
void UpdateNameNumbers(std::vector<std::string> *_name_numbers);
|
||||
|
||||
t_CNNType getCNNType(){return CNNType;};
|
||||
|
||||
@@ -41,8 +41,6 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
_classname = "ClassFlowAlignment";
|
||||
}
|
||||
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 = "ClassFlowCNNGeneral";
|
||||
}
|
||||
if ((_stepname.compare("[Analog]") == 0) || (_stepname.compare(";[Analog]") == 0)){
|
||||
@@ -51,6 +49,9 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
|
||||
if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)){
|
||||
_classname = "ClassFlowMQTT";
|
||||
}
|
||||
if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)){
|
||||
_classname = "ClassFlowInfluxDB";
|
||||
}
|
||||
|
||||
for (int i = 0; i < FlowControll.size(); ++i)
|
||||
if (FlowControll[i]->name().compare(_classname) == 0){
|
||||
@@ -69,14 +70,16 @@ std::string ClassFlowControll::TranslateAktstatus(std::string _input)
|
||||
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("ClassFlowInfluxDB") == 0)
|
||||
return ("Sending InfluxDB");
|
||||
if (_input.compare("ClassFlowPostProcessing") == 0)
|
||||
return ("Processing");
|
||||
if (_input.compare("ClassFlowWriteList") == 0)
|
||||
return ("Processing");
|
||||
|
||||
return "Unkown Status";
|
||||
}
|
||||
@@ -182,7 +185,13 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
|
||||
}
|
||||
if (toUpper(_type).compare("[MQTT]") == 0)
|
||||
cfc = new ClassFlowMQTT(&FlowControll);
|
||||
|
||||
if (toUpper(_type).compare("[INFLUXDB]") == 0)
|
||||
cfc = new ClassFlowInfluxDB(&FlowControll);
|
||||
|
||||
if (toUpper(_type).compare("[WRITELIST]") == 0)
|
||||
cfc = new ClassFlowWriteList(&FlowControll);
|
||||
|
||||
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
|
||||
{
|
||||
cfc = new ClassFlowPostProcessing(&FlowControll, flowanalog, flowdigit);
|
||||
@@ -296,6 +305,7 @@ bool ClassFlowControll::doFlow(string time)
|
||||
if (i) i -= 1; // vorheriger Schritt muss wiederholt werden (vermutlich Bilder aufnehmen)
|
||||
result = false;
|
||||
if (repeat > 5) {
|
||||
LogFile.SwitchOnOff(true);
|
||||
LogFile.WriteToFile("Wiederholung 5x nicht erfolgreich --> reboot");
|
||||
doReboot();
|
||||
// Schritt wurde 5x wiederholt --> reboot
|
||||
@@ -329,7 +339,7 @@ string ClassFlowControll::getReadoutAll(int _type)
|
||||
out = out + (*numbers)[i]->name + "\t";
|
||||
switch (_type) {
|
||||
case READOUT_TYPE_VALUE:
|
||||
out = out + (*numbers)[i]->ReturnValueNoError;
|
||||
out = out + (*numbers)[i]->ReturnValue;
|
||||
break;
|
||||
case READOUT_TYPE_PREVALUE:
|
||||
if (flowpostprocessing->PreValueUse)
|
||||
@@ -484,6 +494,8 @@ bool ClassFlowControll::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
// reboot notwendig damit die neue wlan.ini auch benutzt wird !!!
|
||||
fclose(pfile);
|
||||
printf("do reboot\n");
|
||||
LogFile.SwitchOnOff(true);
|
||||
LogFile.WriteToFile("Reboot to activate new HOSTNAME.");
|
||||
esp_restart();
|
||||
hard_restart();
|
||||
doReboot();
|
||||
@@ -577,6 +589,8 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
{
|
||||
std::vector<HTMLInfo*> htmlinfo;
|
||||
htmlinfo = GetAllDigital();
|
||||
printf("After getClassFlowControll::GetAllDigital\n");
|
||||
|
||||
for (int i = 0; i < htmlinfo.size(); ++i)
|
||||
{
|
||||
if (_fn == htmlinfo[i]->filename)
|
||||
@@ -634,35 +648,7 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowControll::getJSON()
|
||||
string ClassFlowControll::getJSON(std::string _id, std::string _mac)
|
||||
{
|
||||
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;
|
||||
return flowpostprocessing->GetJSON(_id, _mac);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
#include "ClassFlowMQTT.h"
|
||||
#include "ClassFlowInfluxDB.h"
|
||||
#include "ClassFlowCNNGeneral.h"
|
||||
#include "ClassFlowWriteList.h"
|
||||
|
||||
|
||||
#define READOUT_TYPE_VALUE 0
|
||||
@@ -48,7 +50,7 @@ public:
|
||||
string UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern);
|
||||
string GetPrevalue(std::string _number = "");
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
string getJSON();
|
||||
string getJSON(std::string _id = "", std::string _mac = "");
|
||||
|
||||
string TranslateAktstatus(std::string _input);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ struct roi {
|
||||
int posx, posy, deltax, deltay;
|
||||
float result_float;
|
||||
int result_klasse;
|
||||
bool isReject;
|
||||
string name;
|
||||
CImageBasis *image, *image_org;
|
||||
};
|
||||
@@ -16,10 +17,16 @@ struct general {
|
||||
std::vector<roi*> ROI;
|
||||
};
|
||||
|
||||
enum t_RateType {
|
||||
AbsoluteChange,
|
||||
RateChange
|
||||
};
|
||||
|
||||
|
||||
struct NumberPost {
|
||||
float MaxRateValue;
|
||||
bool useMaxRateValue;
|
||||
t_RateType RateType;
|
||||
bool ErrorMessage;
|
||||
bool PreValueOkay;
|
||||
bool AllowNegativeRates;
|
||||
@@ -30,10 +37,10 @@ struct NumberPost {
|
||||
float PreValue; // letzter Wert, der gut ausgelesen wurde
|
||||
float Value; // letzer ausgelesener Wert, inkl. Korrekturen
|
||||
string ReturnRateValue; // RückgabewertRate
|
||||
string ReturnChangeAbsolute; // 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 ReturnPreValue; // korrigierter Rückgabewert ohne Fehlermeldung
|
||||
string ErrorMessageText; // Fehlermeldung bei Consistency Check
|
||||
int AnzahlAnalog;
|
||||
int AnzahlDigital;
|
||||
|
||||
161
code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp
Normal file
161
code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include <sstream>
|
||||
#include "ClassFlowInfluxDB.h"
|
||||
#include "Helper.h"
|
||||
#include "connect_wlan.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
#include "interface_influxdb.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
void ClassFlowInfluxDB::SetInitialParameter(void)
|
||||
{
|
||||
uri = "";
|
||||
database = "";
|
||||
measurement = "";
|
||||
|
||||
OldValue = "";
|
||||
flowpostprocessing = NULL;
|
||||
user = "";
|
||||
password = "";
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
disabled = false;
|
||||
InfluxDBenable = false;
|
||||
}
|
||||
|
||||
ClassFlowInfluxDB::ClassFlowInfluxDB()
|
||||
{
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
ListFlowControll = lfc;
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc, ClassFlow *_prev)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
previousElement = _prev;
|
||||
ListFlowControll = lfc;
|
||||
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowInfluxDB::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).compare("[INFLUXDB]") != 0)
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
printf("while loop reading line: %s\n", aktparamgraph.c_str());
|
||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||
if ((toUpper(zerlegt[0]) == "USER") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->user = zerlegt[1];
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "PASSWORD") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->password = zerlegt[1];
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "URI") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->uri = zerlegt[1];
|
||||
}
|
||||
if (((toUpper(zerlegt[0]) == "MEASUREMENT")) && (zerlegt.size() > 1))
|
||||
{
|
||||
this->measurement = zerlegt[1];
|
||||
}
|
||||
if (((toUpper(zerlegt[0]) == "DATABASE")) && (zerlegt.size() > 1))
|
||||
{
|
||||
this->database = zerlegt[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ((uri.length() > 0) && (database.length() > 0) && (measurement.length() > 0))
|
||||
{
|
||||
printf("Init InfluxDB with uri: %s, measurement: %s, user: %s, password: %s\n", uri.c_str(), measurement.c_str(), user.c_str(), password.c_str());
|
||||
InfluxDBInit(uri, database, measurement, user, password);
|
||||
InfluxDBenable = true;
|
||||
} else {
|
||||
printf("InfluxDB init skipped as we are missing some parameters");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
string ClassFlowInfluxDB::GetInfluxDBMeasurement()
|
||||
{
|
||||
return measurement;
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowInfluxDB::doFlow(string zwtime)
|
||||
{
|
||||
if (!InfluxDBenable)
|
||||
return true;
|
||||
|
||||
std::string result;
|
||||
std::string resulterror = "";
|
||||
std::string resultraw = "";
|
||||
std::string resultrate = "";
|
||||
std::string resulttimestamp = "";
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
result = (*NUMBERS)[i]->ReturnValue;
|
||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
if (namenumber == "default")
|
||||
namenumber = "value";
|
||||
else
|
||||
namenumber = namenumber + "/value";
|
||||
|
||||
if (result.length() > 0 && resulttimestamp.length() > 0)
|
||||
InfluxDBPublish(namenumber, result, resulttimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
OldValue = result;
|
||||
|
||||
return true;
|
||||
}
|
||||
31
code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h
Normal file
31
code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "ClassFlow.h"
|
||||
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClassFlowInfluxDB :
|
||||
public ClassFlow
|
||||
{
|
||||
protected:
|
||||
std::string uri, database, measurement;
|
||||
std::string OldValue;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
std::string user, password;
|
||||
bool InfluxDBenable;
|
||||
|
||||
void SetInitialParameter(void);
|
||||
|
||||
public:
|
||||
ClassFlowInfluxDB();
|
||||
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc);
|
||||
ClassFlowInfluxDB(std::vector<ClassFlow*>* lfc, ClassFlow *_prev);
|
||||
|
||||
string GetInfluxDBMeasurement();
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string name(){return "ClassFlowInfluxDB";};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <sstream>
|
||||
#include "ClassFlowMQTT.h"
|
||||
#include "Helper.h"
|
||||
#include "connect_wlan.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
#include "interface_mqtt.h"
|
||||
@@ -24,7 +25,8 @@ void ClassFlowMQTT::SetInitialParameter(void)
|
||||
OldValue = "";
|
||||
flowpostprocessing = NULL;
|
||||
user = "";
|
||||
password = "";
|
||||
password = "";
|
||||
SetRetainFlag = 0;
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
disabled = false;
|
||||
@@ -98,6 +100,12 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
this->uri = zerlegt[1];
|
||||
}
|
||||
if ((toUpper(zerlegt[0]) == "SETRETAINFLAG") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
SetRetainFlag = 1;
|
||||
}
|
||||
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "CLIENTID") && (zerlegt.size() > 1))
|
||||
{
|
||||
@@ -110,11 +118,14 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
}
|
||||
}
|
||||
|
||||
printf("Init Read with uri: %s, clientname: %s, user: %s, password: %s, maintopic: %s\n", uri.c_str(), clientname.c_str(), user.c_str(), password.c_str(), mainerrortopic.c_str());
|
||||
if (!MQTTisConnected() && (uri.length() > 0) && (maintopic.length() > 0))
|
||||
{
|
||||
{
|
||||
printf("InitMQTTInit\n");
|
||||
mainerrortopic = maintopic + "/connection";
|
||||
printf("Init MQTT with uri: %s, clientname: %s, user: %s, password: %s, maintopic: %s\n", uri.c_str(), clientname.c_str(), user.c_str(), password.c_str(), mainerrortopic.c_str());
|
||||
MQTTInit(uri, clientname, user, password, mainerrortopic, 60);
|
||||
MQTTPublish(mainerrortopic, "connected");
|
||||
MQTTPublish(mainerrortopic, "connected", SetRetainFlag);
|
||||
MQTTenable = true;
|
||||
}
|
||||
|
||||
@@ -138,6 +149,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
std::string resultraw = "";
|
||||
std::string resultrate = "";
|
||||
std::string resulttimestamp = "";
|
||||
std::string resultchangabs = "";
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
|
||||
@@ -146,12 +158,18 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
zw = maintopic + "/" + "uptime";
|
||||
char uptimeStr[11];
|
||||
sprintf(uptimeStr, "%ld", (long)getUpTime());
|
||||
MQTTPublish(zw, uptimeStr);
|
||||
MQTTPublish(zw, uptimeStr, SetRetainFlag);
|
||||
|
||||
zw = maintopic + "/" + "freeMem";
|
||||
char freeheapmem[11];
|
||||
sprintf(freeheapmem, "%zu", esp_get_free_heap_size());
|
||||
MQTTPublish(zw, freeheapmem);
|
||||
MQTTPublish(zw, freeheapmem, SetRetainFlag);
|
||||
|
||||
zw = maintopic + "/" + "wifiRSSI";
|
||||
char rssi[11];
|
||||
sprintf(rssi, "%d", get_WIFI_RSSI());
|
||||
MQTTPublish(zw, rssi, SetRetainFlag);
|
||||
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
@@ -159,10 +177,11 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
result = (*NUMBERS)[i]->ReturnValueNoError;
|
||||
result = (*NUMBERS)[i]->ReturnValue;
|
||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||
resultchangabs = (*NUMBERS)[i]->ReturnChangeAbsolute;
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
|
||||
namenumber = (*NUMBERS)[i]->name;
|
||||
@@ -173,23 +192,27 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
|
||||
zw = namenumber + "value";
|
||||
if (result.length() > 0)
|
||||
MQTTPublish(zw, result);
|
||||
MQTTPublish(zw, result, SetRetainFlag);
|
||||
|
||||
zw = namenumber + "error";
|
||||
if (resulterror.length() > 0)
|
||||
MQTTPublish(zw, resulterror, 1);
|
||||
MQTTPublish(zw, resulterror, SetRetainFlag);
|
||||
|
||||
zw = namenumber + "rate";
|
||||
if (resultrate.length() > 0)
|
||||
MQTTPublish(zw, resultrate);
|
||||
MQTTPublish(zw, resultrate, SetRetainFlag);
|
||||
|
||||
zw = namenumber + "changeabsolut";
|
||||
if (resultchangabs.length() > 0)
|
||||
MQTTPublish(zw, resultchangabs, SetRetainFlag);
|
||||
|
||||
zw = namenumber + "raw";
|
||||
if (resultraw.length() > 0)
|
||||
MQTTPublish(zw, resultraw);
|
||||
MQTTPublish(zw, resultraw, SetRetainFlag);
|
||||
|
||||
zw = namenumber + "timestamp";
|
||||
if (resulttimestamp.length() > 0)
|
||||
MQTTPublish(zw, resulttimestamp);
|
||||
MQTTPublish(zw, resulttimestamp, SetRetainFlag);
|
||||
|
||||
|
||||
std::string json = "";
|
||||
@@ -208,7 +231,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
json += ",\"timestamp\":\""+resulttimestamp+"\"}";
|
||||
|
||||
zw = namenumber + "json";
|
||||
MQTTPublish(zw, json);
|
||||
MQTTPublish(zw, json, SetRetainFlag);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -224,7 +247,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
|
||||
result = result + "\t" + zw;
|
||||
}
|
||||
}
|
||||
MQTTPublish(topic, result);
|
||||
MQTTPublish(topic, result, SetRetainFlag);
|
||||
}
|
||||
|
||||
OldValue = result;
|
||||
|
||||
@@ -13,6 +13,7 @@ protected:
|
||||
std::string OldValue;
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
std::string user, password;
|
||||
int SetRetainFlag;
|
||||
bool MQTTenable;
|
||||
|
||||
std::string maintopic, mainerrortopic;
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
#include "CImageBasis.h"
|
||||
#include "ClassControllCamera.h"
|
||||
|
||||
#include "esp_wifi.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
// #define DEBUG_DETAIL_ON
|
||||
|
||||
// #define WIFITURNOFF
|
||||
|
||||
static const char* TAG = "flow_make_image";
|
||||
|
||||
esp_err_t ClassFlowMakeImage::camera_capture(){
|
||||
@@ -118,7 +122,15 @@ bool ClassFlowMakeImage::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
if ((toUpper(zerlegt[0]) == "FIXEDEXPOSURE") && (zerlegt.size() > 1))
|
||||
{
|
||||
if (toUpper(zerlegt[1]) == "TRUE")
|
||||
FixedExposure = true;
|
||||
FixedExposure = true;
|
||||
}
|
||||
|
||||
if ((toUpper(zerlegt[0]) == "LEDINTENSITY") && (zerlegt.size() > 1))
|
||||
{
|
||||
float ledintensity = stof(zerlegt[1]);
|
||||
ledintensity = min((float) 100, ledintensity);
|
||||
ledintensity = max((float) 0, ledintensity);
|
||||
Camera.SetLEDIntensity(ledintensity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,8 +174,18 @@ bool ClassFlowMakeImage::doFlow(string zwtime)
|
||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - Before takePictureWithFlash");
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WIFITURNOFF
|
||||
esp_wifi_stop(); // to save power usage and
|
||||
#endif
|
||||
|
||||
takePictureWithFlash(flashdauer);
|
||||
|
||||
#ifdef WIFITURNOFF
|
||||
esp_wifi_start();
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DEBUG_DETAIL_ON
|
||||
LogFile.WriteHeapInfo("ClassFlowMakeImage::doFlow - After takePictureWithFlash");
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,42 @@
|
||||
#define PREVALUE_TIME_FORMAT_INPUT "%d-%d-%dT%d:%d:%d"
|
||||
|
||||
|
||||
std::string ClassFlowPostProcessing::GetJSON(std::string _id, std::string _mac, std::string _lineend)
|
||||
{
|
||||
std::string json="{" + _lineend;
|
||||
|
||||
for (int i = 0; i < NUMBERS.size(); ++i)
|
||||
{
|
||||
json += "\"" + NUMBERS[i]->name + "\":" + _lineend;
|
||||
json += " {" + _lineend;
|
||||
|
||||
if (_id.length() > 0)
|
||||
json += " \"ID\": \"" + _id + "\"," + _lineend;
|
||||
if (_mac.length() > 0)
|
||||
json += " \"MAC\": \"" + _mac + "\"," + _lineend;
|
||||
|
||||
if (NUMBERS[i]->ReturnValue.length() > 0)
|
||||
json += " \"value\": \"" + NUMBERS[i]->ReturnValue + "\"," + _lineend;
|
||||
else
|
||||
json += " \"value\": \"\"," + _lineend;
|
||||
json += " \"raw\": \"" + NUMBERS[i]->ReturnRawValue + "\"," + _lineend;
|
||||
json += " \"error\": \"" + NUMBERS[i]->ErrorMessageText + "\"," + _lineend;
|
||||
if (NUMBERS[i]->ReturnRateValue.length() > 0)
|
||||
json += " \"rate\": " + NUMBERS[i]->ReturnRateValue + "," + _lineend;
|
||||
else
|
||||
json += " \"rate\": \"\"," + _lineend;
|
||||
|
||||
json += " \"timestamp\": \"" + NUMBERS[i]->timeStamp + "\"" + _lineend;
|
||||
if ((i+1) < NUMBERS.size())
|
||||
json += " }," + _lineend;
|
||||
else
|
||||
json += " }" + _lineend;
|
||||
}
|
||||
json += "}";
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
string ClassFlowPostProcessing::GetPreValue(std::string _number)
|
||||
{
|
||||
std::string result;
|
||||
@@ -41,6 +77,8 @@ void ClassFlowPostProcessing::SetPreValue(float zw, string _numbers, bool _exter
|
||||
if (NUMBERS[j]->name == _numbers)
|
||||
{
|
||||
NUMBERS[j]->PreValue = zw;
|
||||
NUMBERS[j]->ReturnPreValue = std::to_string(zw);
|
||||
NUMBERS[j]->PreValueOkay = true;
|
||||
if (_extern)
|
||||
{
|
||||
time(&(NUMBERS[j]->lastvalue));
|
||||
@@ -111,25 +149,9 @@ bool ClassFlowPostProcessing::LoadPreValue(void)
|
||||
double difference = difftime(tStart, NUMBERS[j]->lastvalue);
|
||||
difference /= 60;
|
||||
if (difference > PreValueAgeStartup)
|
||||
{
|
||||
NUMBERS[j]->PreValueOkay = false;
|
||||
}
|
||||
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]->Nachkomma + 1); // SIcherheitshalber 1 Stelle mehr, da ggf. Exgtended Resolution an ist (wird erst beim ersten Durchlauf gesetzt)
|
||||
NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnValue;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,12 +205,10 @@ bool ClassFlowPostProcessing::LoadPreValue(void)
|
||||
|
||||
NUMBERS[0]->Value = NUMBERS[0]->PreValue;
|
||||
NUMBERS[0]->ReturnValue = to_string(NUMBERS[0]->Value);
|
||||
NUMBERS[0]->ReturnValueNoError = NUMBERS[0]->ReturnValue;
|
||||
|
||||
if (NUMBERS[0]->digit_roi || NUMBERS[0]->analog_roi)
|
||||
{
|
||||
NUMBERS[0]->ReturnValue = RundeOutput(NUMBERS[0]->Value, NUMBERS[0]->Nachkomma);
|
||||
NUMBERS[0]->ReturnValueNoError = NUMBERS[0]->ReturnValue;
|
||||
}
|
||||
|
||||
UpdatePreValueINI = true; // Konvertierung ins neue Format
|
||||
@@ -218,8 +238,9 @@ void ClassFlowPostProcessing::SavePreValue()
|
||||
|
||||
_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);
|
||||
if (pFile) {
|
||||
fputs(_zw.c_str(), pFile);
|
||||
}
|
||||
}
|
||||
|
||||
UpdatePreValueINI = false;
|
||||
@@ -320,6 +341,39 @@ void ClassFlowPostProcessing::handleDecimalSeparator(string _decsep, string _val
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClassFlowPostProcessing::handleMaxRateType(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)
|
||||
{
|
||||
t_RateType _rt = AbsoluteChange;
|
||||
|
||||
if (toUpper(_value) == "RATECHANGE")
|
||||
_rt = RateChange;
|
||||
|
||||
if (_digit == "default") // erstmal auf default setzen (falls sonst nichts gesetzt)
|
||||
{
|
||||
NUMBERS[j]->RateType = _rt;
|
||||
}
|
||||
|
||||
if (NUMBERS[j]->name == _digit)
|
||||
{
|
||||
NUMBERS[j]->RateType = _rt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClassFlowPostProcessing::handleMaxRateValue(string _decsep, string _value)
|
||||
{
|
||||
string _digit, _decpos;
|
||||
@@ -395,6 +449,10 @@ bool ClassFlowPostProcessing::ReadParameter(FILE* pfile, string& aktparamgraph)
|
||||
{
|
||||
handleMaxRateValue(zerlegt[0], zerlegt[1]);
|
||||
}
|
||||
if ((toUpper(_param) == "MAXRATETYPE") && (zerlegt.size() > 1))
|
||||
{
|
||||
handleMaxRateType(zerlegt[0], zerlegt[1]);
|
||||
}
|
||||
|
||||
if ((toUpper(_param) == "PREVALUEUSE") && (zerlegt.size() > 1))
|
||||
{
|
||||
@@ -486,12 +544,12 @@ void ClassFlowPostProcessing::InitNUMBERS()
|
||||
|
||||
_number->ReturnRawValue = ""; // Rohwert (mit N & führenden 0)
|
||||
_number->ReturnValue = ""; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
|
||||
_number->ReturnValueNoError = ""; // korrigierter Rückgabewert ohne Fehlermeldung
|
||||
_number->ErrorMessageText = ""; // Fehlermeldung bei Consistency Check
|
||||
_number->ReturnPreValue = "";
|
||||
_number->PreValueOkay = false;
|
||||
_number->AllowNegativeRates = false;
|
||||
_number->MaxRateValue = 0.1;
|
||||
_number->RateType = AbsoluteChange;
|
||||
_number->useMaxRateValue = false;
|
||||
_number->checkDigitIncreaseConsistency = false;
|
||||
_number->DecimalShift = 0;
|
||||
@@ -504,7 +562,6 @@ void ClassFlowPostProcessing::InitNUMBERS()
|
||||
_number->Value = 0; // letzer ausgelesener Wert, inkl. Korrekturen
|
||||
_number->ReturnRawValue = ""; // Rohwert (mit N & führenden 0)
|
||||
_number->ReturnValue = ""; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
|
||||
_number->ReturnValueNoError = ""; // korrigierter Rückgabewert ohne Fehlermeldung
|
||||
_number->ErrorMessageText = ""; // Fehlermeldung bei Consistency Check
|
||||
|
||||
_number->Nachkomma = _number->AnzahlAnalog;
|
||||
@@ -512,8 +569,10 @@ void ClassFlowPostProcessing::InitNUMBERS()
|
||||
NUMBERS.push_back(_number);
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUMBERS.size(); ++i)
|
||||
for (int i = 0; i < NUMBERS.size(); ++i) {
|
||||
printf("Number %s, Anz DIG: %d, Anz ANA %d\n", NUMBERS[i]->name.c_str(), NUMBERS[i]->AnzahlDigital, NUMBERS[i]->AnzahlAnalog);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
string ClassFlowPostProcessing::ShiftDecimal(string in, int _decShift){
|
||||
@@ -585,115 +644,115 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
|
||||
for (int j = 0; j < NUMBERS.size(); ++j)
|
||||
{
|
||||
NUMBERS[j]->ReturnRawValue = "";
|
||||
NUMBERS[j]->ReturnRateValue = "";
|
||||
NUMBERS[j]->ReturnValue = "";
|
||||
NUMBERS[j]->ErrorMessageText = "";
|
||||
NUMBERS[j]->Value = -1;
|
||||
|
||||
UpdateNachkommaDecimalShift();
|
||||
|
||||
int previous_value = -1;
|
||||
|
||||
if (NUMBERS[j]->analog_roi)
|
||||
{
|
||||
NUMBERS[j]->ReturnRawValue = flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution);
|
||||
if (NUMBERS[j]->ReturnRawValue.length() > 0)
|
||||
{
|
||||
char zw = NUMBERS[j]->ReturnRawValue[0];
|
||||
if (zw >= 48 && zw <=57)
|
||||
previous_value = zw - 48;
|
||||
}
|
||||
}
|
||||
|
||||
if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi)
|
||||
NUMBERS[j]->ReturnRawValue = "." + NUMBERS[j]->ReturnRawValue;
|
||||
|
||||
if (NUMBERS[j]->digit_roi)
|
||||
{
|
||||
if (NUMBERS[j]->analog_roi)
|
||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false);
|
||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false, previous_value, NUMBERS[j]->analog_roi->ROI[0]->result_float) + NUMBERS[j]->ReturnRawValue;
|
||||
else
|
||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution); // Extended Resolution nur falls es keine analogen Ziffern gibt
|
||||
NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution, previous_value); // 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]->isExtendedResolution);
|
||||
NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
|
||||
|
||||
NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
|
||||
printf("ReturnRaw %s", NUMBERS[j]->ReturnRawValue.c_str());
|
||||
|
||||
|
||||
if (IgnoreLeadingNaN)
|
||||
{
|
||||
while ((NUMBERS[j]->ReturnRawValue.length() > 1) && (NUMBERS[j]->ReturnRawValue[0] == 'N'))
|
||||
{
|
||||
NUMBERS[j]->ReturnRawValue.erase(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
rohwert = NUMBERS[j]->ReturnRawValue;
|
||||
NUMBERS[j]->ReturnValue = NUMBERS[j]->ReturnRawValue;
|
||||
|
||||
if (!PreValueUse || !NUMBERS[j]->PreValueOkay)
|
||||
if (findDelimiterPos(NUMBERS[j]->ReturnValue, "N") != std::string::npos)
|
||||
{
|
||||
NUMBERS[j]->ReturnValue = NUMBERS[j]->ReturnRawValue;
|
||||
NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnRawValue;
|
||||
|
||||
if ((findDelimiterPos(NUMBERS[j]->ReturnValue, "N") == std::string::npos) && (NUMBERS[j]->ReturnValue.length() > 0))
|
||||
{
|
||||
while ((NUMBERS[j]->ReturnValue.length() > 1) && (NUMBERS[j]->ReturnValue[0] == '0'))
|
||||
{
|
||||
NUMBERS[j]->ReturnValue.erase(0, 1);
|
||||
}
|
||||
NUMBERS[j]->Value = std::stof(NUMBERS[j]->ReturnValue);
|
||||
NUMBERS[j]->ReturnValueNoError = NUMBERS[j]->ReturnValue;
|
||||
|
||||
NUMBERS[j]->PreValueOkay = true;
|
||||
NUMBERS[j]->PreValue = NUMBERS[j]->Value;
|
||||
NUMBERS[j]->ReturnPreValue = RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
|
||||
NUMBERS[j]->lastvalue = flowMakeImage->getTimeImageTaken();
|
||||
zwtime = ConvertTimeToString(NUMBERS[j]->lastvalue, PREVALUE_TIME_FORMAT_OUTPUT);
|
||||
|
||||
UpdatePreValueINI = true;
|
||||
SavePreValue();
|
||||
}
|
||||
if (PreValueUse && NUMBERS[j]->PreValueOkay)
|
||||
NUMBERS[j]->ReturnValue = ErsetzteN(NUMBERS[j]->ReturnValue, NUMBERS[j]->PreValue);
|
||||
else
|
||||
continue; // es gibt keinen Zahl, da noch ein N vorhanden ist.
|
||||
}
|
||||
else
|
||||
|
||||
// Lösche führende Nullen (außer es ist nur noch einen 0)
|
||||
while ((NUMBERS[j]->ReturnValue.length() > 1) && (NUMBERS[j]->ReturnValue[0] == '0'))
|
||||
NUMBERS[j]->ReturnValue.erase(0, 1);
|
||||
|
||||
NUMBERS[j]->Value = std::stof(NUMBERS[j]->ReturnValue);
|
||||
|
||||
if (NUMBERS[j]->checkDigitIncreaseConsistency)
|
||||
{
|
||||
zw = ErsetzteN(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->PreValue);
|
||||
NUMBERS[j]->Value = checkDigitConsistency(NUMBERS[j]->Value, NUMBERS[j]->DecimalShift, NUMBERS[j]->analog_roi != NULL, NUMBERS[j]->PreValue);
|
||||
}
|
||||
|
||||
NUMBERS[j]->Value = std::stof(zw);
|
||||
if (NUMBERS[j]->checkDigitIncreaseConsistency)
|
||||
{
|
||||
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]->Nachkomma);
|
||||
|
||||
if ((!NUMBERS[j]->AllowNegativeRates) && (NUMBERS[j]->Value < NUMBERS[j]->PreValue))
|
||||
if (!NUMBERS[j]->AllowNegativeRates)
|
||||
{
|
||||
if (NUMBERS[j]->Value < NUMBERS[j]->PreValue)
|
||||
{
|
||||
NUMBERS[j]->ErrorMessageText = NUMBERS[j]->ErrorMessageText + "Neg. Rate - Read: " + zwvalue + " - Raw: " + NUMBERS[j]->ReturnRawValue + " - Pre: " + RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma) + " ";
|
||||
NUMBERS[j]->Value = NUMBERS[j]->PreValue;
|
||||
zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma);
|
||||
NUMBERS[j]->ReturnValue = "";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
double difference = difftime(imagetime, NUMBERS[j]->lastvalue); // in Sekunden
|
||||
difference /= 60;
|
||||
NUMBERS[j]->FlowRateAct = (NUMBERS[j]->Value - NUMBERS[j]->PreValue) / difference;
|
||||
NUMBERS[j]->ReturnRateValue = to_string(NUMBERS[j]->FlowRateAct);
|
||||
|
||||
if (NUMBERS[j]->useMaxRateValue && PreValueUse && NUMBERS[j]->PreValueOkay)
|
||||
{
|
||||
float _ratedifference;
|
||||
if (NUMBERS[j]->RateType == RateChange)
|
||||
_ratedifference = NUMBERS[j]->FlowRateAct;
|
||||
else
|
||||
_ratedifference = (NUMBERS[j]->Value - NUMBERS[j]->PreValue);
|
||||
|
||||
if (abs(_ratedifference) > abs(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;
|
||||
zwvalue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma);
|
||||
}
|
||||
|
||||
NUMBERS[j]->ReturnValueNoError = zwvalue;
|
||||
NUMBERS[j]->ReturnValue = zwvalue;
|
||||
if (NUMBERS[j]->ErrorMessage && (NUMBERS[j]->ErrorMessageText.length() > 0))
|
||||
NUMBERS[j]->ReturnValue = NUMBERS[j]->ReturnValue + "\t" + NUMBERS[j]->ErrorMessageText;
|
||||
|
||||
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 = "";
|
||||
|
||||
NUMBERS[j]->ReturnRateValue = "";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
NUMBERS[j]->ReturnChangeAbsolute = RundeOutput(NUMBERS[j]->Value - NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
|
||||
NUMBERS[j]->lastvalue = imagetime;
|
||||
NUMBERS[j]->PreValue = NUMBERS[j]->Value;
|
||||
NUMBERS[j]->PreValueOkay = true;
|
||||
|
||||
|
||||
NUMBERS[j]->ReturnValue = RundeOutput(NUMBERS[j]->Value, NUMBERS[j]->Nachkomma);
|
||||
NUMBERS[j]->ReturnPreValue = RundeOutput(NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
|
||||
|
||||
NUMBERS[j]->ErrorMessageText = "no error";
|
||||
UpdatePreValueINI = true;
|
||||
|
||||
string _zw = "PostProcessing - Raw: " + NUMBERS[j]->ReturnRawValue + " Value: " + NUMBERS[j]->ReturnValue + " Error: " + NUMBERS[j]->ErrorMessageText;
|
||||
LogFile.WriteToFile(_zw);
|
||||
}
|
||||
@@ -755,7 +814,7 @@ string ClassFlowPostProcessing::getReadoutParam(bool _rawValue, bool _noerror, i
|
||||
if (_rawValue)
|
||||
return NUMBERS[_number]->ReturnRawValue;
|
||||
if (_noerror)
|
||||
return NUMBERS[_number]->ReturnValueNoError;
|
||||
return NUMBERS[_number]->ReturnValue;
|
||||
return NUMBERS[_number]->ReturnValue;
|
||||
}
|
||||
|
||||
@@ -831,14 +890,14 @@ float ClassFlowPostProcessing::checkDigitConsistency(float input, int _decilamsh
|
||||
while (pot <= pot_max)
|
||||
{
|
||||
zw = input / pow(10, pot-1);
|
||||
aktdigit_before = ((int) zw + 10) % 10;
|
||||
aktdigit_before = ((int) zw) % 10;
|
||||
zw = _preValue / pow(10, pot-1);
|
||||
olddigit_before = ((int) zw + 10) % 10;
|
||||
olddigit_before = ((int) zw) % 10;
|
||||
|
||||
zw = input / pow(10, pot);
|
||||
aktdigit = ((int) zw + 10) % 10;
|
||||
aktdigit = ((int) zw) % 10;
|
||||
zw = _preValue / pow(10, pot);
|
||||
olddigit = ((int) zw + 10) % 10;
|
||||
olddigit = ((int) zw) % 10;
|
||||
|
||||
no_nulldurchgang = (olddigit_before <= aktdigit_before);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
class ClassFlowPostProcessing :
|
||||
public ClassFlow
|
||||
{
|
||||
@@ -38,7 +39,9 @@ protected:
|
||||
void InitNUMBERS();
|
||||
void handleDecimalSeparator(string _decsep, string _value);
|
||||
void handleMaxRateValue(string _decsep, string _value);
|
||||
void handleDecimalExtendedResolution(string _decsep, string _value);
|
||||
void handleDecimalExtendedResolution(string _decsep, string _value);
|
||||
void handleMaxRateType(string _decsep, string _value);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,6 +60,8 @@ public:
|
||||
string GetPreValue(std::string _number = "");
|
||||
void SetPreValue(float zw, string _numbers, bool _extern = false);
|
||||
|
||||
std::string GetJSON(std::string _id = "", std::string _mac = "", std::string _lineend = "\n");
|
||||
|
||||
void UpdateNachkommaDecimalShift();
|
||||
|
||||
std::vector<NumberPost*>* GetNumbers(){return &NUMBERS;};
|
||||
|
||||
97
code/components/jomjol_flowcontroll/ClassFlowWriteList.cpp
Normal file
97
code/components/jomjol_flowcontroll/ClassFlowWriteList.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include <sstream>
|
||||
#include "ClassFlowWriteList.h"
|
||||
#include "Helper.h"
|
||||
|
||||
#include "time_sntp.h"
|
||||
|
||||
|
||||
#include <time.h>
|
||||
|
||||
void ClassFlowWriteList::SetInitialParameter(void)
|
||||
{
|
||||
flowpostprocessing = NULL;
|
||||
previousElement = NULL;
|
||||
ListFlowControll = NULL;
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
ClassFlowWriteList::ClassFlowWriteList()
|
||||
{
|
||||
SetInitialParameter();
|
||||
}
|
||||
|
||||
ClassFlowWriteList::ClassFlowWriteList(std::vector<ClassFlow*>* lfc)
|
||||
{
|
||||
SetInitialParameter();
|
||||
|
||||
ListFlowControll = lfc;
|
||||
for (int i = 0; i < ListFlowControll->size(); ++i)
|
||||
{
|
||||
if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
|
||||
{
|
||||
flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ClassFlowWriteList::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).compare("[MQTT]") != 0) // Paragraph passt nich zu MakeImage
|
||||
return false;
|
||||
|
||||
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
|
||||
{
|
||||
zerlegt = this->ZerlegeZeile(aktparamgraph);
|
||||
/*
|
||||
if ((toUpper(zerlegt[0]) == "USER") && (zerlegt.size() > 1))
|
||||
{
|
||||
this->user = zerlegt[1];
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ClassFlowWriteList::doFlow(string zwtime)
|
||||
{
|
||||
std::string line = "";
|
||||
|
||||
std::string result;
|
||||
std::string resulterror = "";
|
||||
std::string resultraw = "";
|
||||
std::string resultrate = "";
|
||||
std::string resulttimestamp = "";
|
||||
string zw = "";
|
||||
string namenumber = "";
|
||||
|
||||
if (flowpostprocessing)
|
||||
{
|
||||
std::vector<NumberPost*>* NUMBERS = flowpostprocessing->GetNumbers();
|
||||
|
||||
for (int i = 0; i < (*NUMBERS).size(); ++i)
|
||||
{
|
||||
result = (*NUMBERS)[i]->ReturnValue;
|
||||
resultraw = (*NUMBERS)[i]->ReturnRawValue;
|
||||
resulterror = (*NUMBERS)[i]->ErrorMessageText;
|
||||
resultrate = (*NUMBERS)[i]->ReturnRateValue;
|
||||
resulttimestamp = (*NUMBERS)[i]->timeStamp;
|
||||
|
||||
line = line + resulttimestamp + "\t" + resultraw + "\t" + result + "\t" + resultraw + "\t" + resultrate + "\t" + resulttimestamp + "\t";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
22
code/components/jomjol_flowcontroll/ClassFlowWriteList.h
Normal file
22
code/components/jomjol_flowcontroll/ClassFlowWriteList.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "ClassFlow.h"
|
||||
#include "ClassFlowPostProcessing.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class ClassFlowWriteList :
|
||||
public ClassFlow
|
||||
{
|
||||
protected:
|
||||
ClassFlowPostProcessing* flowpostprocessing;
|
||||
void SetInitialParameter(void);
|
||||
|
||||
public:
|
||||
ClassFlowWriteList();
|
||||
ClassFlowWriteList(std::vector<ClassFlow*>* lfc);
|
||||
|
||||
bool ReadParameter(FILE* pfile, string& aktparamgraph);
|
||||
bool doFlow(string time);
|
||||
string name(){return "ClassFlowWriteList";};
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ struct RefInfo {
|
||||
int fastalg_max = -1;
|
||||
float fastalg_SAD = -1;
|
||||
float fastalg_SAD_criteria = -1;
|
||||
int alignment_algo = 0; // 0 = "Default" (nur R-Kanal), 1 = "HighAccurity" (RGB-Kanal), 2 = "Fast" (1.x RGB, dann isSimilar)
|
||||
int alignment_algo = 0; // 0 = "Default" (nur R-Kanal), 1 = "HighAccuracy" (RGB-Kanal), 2 = "Fast" (1.x RGB, dann isSimilar)
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -274,6 +274,28 @@ void CImageBasis::drawLine(int x1, int y1, int x2, int y2, int r, int g, int b,
|
||||
}
|
||||
}
|
||||
|
||||
void CImageBasis::drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness)
|
||||
{
|
||||
float deltarad, aktrad;
|
||||
int _thick, _x, _y;
|
||||
int rad = radx;
|
||||
|
||||
if (rady > radx)
|
||||
rad = rady;
|
||||
|
||||
deltarad = 1 / (4 * M_PI * (rad + thickness - 1));
|
||||
|
||||
for (aktrad = 0; aktrad <= (2 * M_PI); aktrad += deltarad)
|
||||
for (_thick = 0; _thick < thickness; ++_thick)
|
||||
{
|
||||
_x = sin(aktrad) * (radx + _thick) + x1;
|
||||
_y = cos(aktrad) * (rady + _thick) + y1;
|
||||
if (isInImage(_x, _y))
|
||||
setPixelColor(_x, _y, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CImageBasis::drawCircle(int x1, int y1, int rad, int r, int g, int b, int thickness)
|
||||
{
|
||||
float deltarad, aktrad;
|
||||
|
||||
@@ -57,6 +57,8 @@ class CImageBasis
|
||||
void drawRect(int x, int y, int dx, int dy, int r = 255, int g = 255, int b = 255, int thickness = 1);
|
||||
void drawLine(int x1, int y1, int x2, int y2, int r, int g, int b, int thickness = 1);
|
||||
void drawCircle(int x1, int y1, int rad, int r, int g, int b, int thickness = 1);
|
||||
void drawEllipse(int x1, int y1, int radx, int rady, int r, int g, int b, int thickness = 1);
|
||||
|
||||
void setPixelColor(int x, int y, int r, int g, int b);
|
||||
void Contrast(float _contrast);
|
||||
bool ImageOkay();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user