diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 91c01c0..60ff103 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -24,7 +24,7 @@ jobs:
with:
repository: jonnor/micropython
path: micropython
- ref: emlearn-micropython-v1.23-2
+ ref: emlearn-micropython-v1.24-1
- name: Install Python dependencies
run: pip install -r requirements.txt
- name: Setup MicroPython X86
diff --git a/README.md b/README.md
index fa95479..0395aa3 100644
--- a/README.md
+++ b/README.md
@@ -14,11 +14,10 @@ particularly well suited for low-compexity and low-power classification tasks.
It can be combined with feature preprocessing, including neural networks to address more complex tasks.
## Status
-**Minimally useful, on some MicroPython ports**
+**Minimally useful**
-- Tested *working* on `x64` (Unix port) and `armv7emsp` (Cortex M4F/M7 / STM32).
-- **Not working** on `armv6m` (Cortex M0 / RP2040). [Issue](https://github.com/emlearn/emlearn-micropython/issues/14)
-- **Not working** on `xtensawin` (ESP32). [Issue](https://github.com/emlearn/emlearn-micropython/issues/12)
+- Tested *working* on `x64` (Unix port) and `xtensawin` (ESP32).
+- Currently *broken* on ARM `armv6m` (Cortex M0 / RP2040). [Issue](https://github.com/emlearn/emlearn-micropython/issues/19)
## Features
@@ -48,7 +47,7 @@ It can be combined with feature preprocessing, including neural networks to addr
Minimally you will need
- Python 3.10+ on host
-- MicroPython 1.23+ running onto your device
+- MicroPython 1.24+ running onto your device
#### Download repository
@@ -62,6 +61,19 @@ git clone https://github.com/emlearn/emlearn-micropython
Start with the instructions in [XOR example](./examples/xor_trees/).
+## Supported versions
+
+At any given point in time, emlearn-micropython only provides pre-built binaries for one MicroPython version.
+In general we strongly encourage people to use the latest version.
+There are no long-term-support or bugfix versions, at this point.
+If you build from source, the current version of emlearn-micropython might also work on a couple of MicroPython versions around the time, but this is not guaranteed.
+
+| MicroPython | emlearn-micropython |
+|------------------| ------------------ |
+| 1.24.x | master |
+| 1.24.x | 0.7.0 |
+| 1.23.x | 0.6.0 |
+
#### Find architecture and .mpy version
The correct .mpy files to use depend on the CPU architecture of your microcontroller,
@@ -70,6 +82,7 @@ as well as the MicroPython version.
| MicroPython version | .mpy version |
|---------------------| ------------- |
| 1.23.x | 6.3 |
+| 1.24.x | 6.3 |
Identify which CPU architecture your device uses.
@@ -90,6 +103,26 @@ Information is also available in the official documentation:
[MicroPython: .mpy files](https://docs.micropython.org/en/latest/reference/mpyfiles.html#versioning-and-compatibility-of-mpy-files)
+## More learning resources
+
+emlearn-micropython and emlearn has been covered in the following presentations.
+
+- Microcontrollers + Machine Learning in 1-2-3 (PyData Global 2024).
+[Slides etc](https://github.com/jonnor/embeddedml/tree/master/presentations/PyDataGlobal2024)
+- Sensor data processing on microcontrollers with MicroPython and emlearn (PyConZA 2024).
+[Slides etc](https://github.com/jonnor/embeddedml/tree/master/presentations/PyConZA2024)
+- 6 years of open source TinyML with emlearn - a scikit-learn for microcontrollers (TinyML EMEA 2024)
+[YouTube video](https://www.youtube.com/watch?v=oG7PjPMA3Is) |
+[Slides etc](https://github.com/jonnor/embeddedml/tree/master/presentations/TinymlEMEA2024)
+- emlearn - Machine Learning for Tiny Embedded Systems (Embedded Online Conference 2024).
+[Youtube video](https://www.youtube.com/watch?v=qamVWmcBdmI) |
+[Slides etc](https://github.com/jonnor/embeddedml/tree/master/presentations/EmbeddedOnlineConference2024)
+- Machine Learning on microcontrollers using MicroPython and emlearn (PyCon DE & PyData Berlin 2024).
+[Slides etc](https://github.com/jonnor/embeddedml/tree/master/presentations/PyDataBerlin2024) |
+[YouTube video](https://www.youtube.com/watch?v=_MGm8sctqjg&t=1311s&pp=ygUSZW1sZWFybiBtaWNyb3B5dGhv).
+
+Here is an overview of resources for [TinyML in general](https://tinyml.seas.harvard.edu/courses/).
+
## Benchmarks
#### UCI handwriting digits
@@ -120,7 +153,7 @@ See [MicroPython: Building native modules](https://docs.micropython.org/en/lates
We assume that micropython is installed in the same place as this repository.
If using another location, adjust `MPY_DIR` accordingly.
-You should be using the latest MicroPython 1.23 (or newer).
+You should be using MicroPython 1.24 (or newer).
#### Build
diff --git a/examples/har_trees/README.md b/examples/har_trees/README.md
index e86787e..2b79a5d 100644
--- a/examples/har_trees/README.md
+++ b/examples/har_trees/README.md
@@ -1,6 +1,8 @@
# Human Activity Recognition with tree-based models
+
+
*Human Activity Recognition* (HAR) is the task of detecting what activities a human is performing based on motion.
This type of activity recognition is using an Inertial Measurement Unit (IMU) carried on the person.
The IMU consists has at least one accelerometer, gyro or magnetometer - or a combination of these.
@@ -11,16 +13,20 @@ The same kind of approach can also be applied to animals (*Animal Activity Recog
which enables tracking of pets, lifestock and wild animals.
This has been used for many kinds of animals - such as cats, dogs, diary cattle.
-The same approach can be used for simple gesture recognition.
+The same approach can be used for simple gesture recognition, at least for repetitive gestures.
## Status
-Working. Tested running on ESP32 with MicroPython 1.23.
+Working. Tested running on ESP32 with MicroPython 1.24.
**NOTE:** This is primarily *example* code for a Human Activity Recognition,
not a generally-useful pretrained model.
-The dataset used is rather simple, and may not reflect the data you get from your device
-- which will lead to poor classifications.
-For a real world usage you should probably replace the dataset with your own data, collected on your own device.
+The dataset used is rather simple, and may not reflect the data you get from your device.
+Such data mismatch will often lead to poor classification results.
+For a real world usage you should aim to replace the dataset with your own data, collected on your own device.
+Or alternatively use a large and diverse dataset from a multiple users, devices and scenarios.
+
+At the bottom on the README there are some instructions and tools for collecting your own data,
+and training a custom model on such a dataset.
## Machine Learning pipeline
@@ -29,26 +35,31 @@ This example uses an approach based on the paper
For each time-window, time-based statistical features are computed,
and then classified with a RandomForest model.
-## Dataset
+## Dataset 1: Common activities (UCI HAR)
+
The example uses the [UCI-HAR dataset](https://www.archive.ics.uci.edu/dataset/341/smartphone+based+recognition+of+human+activities+and+postural+transitions).
The classes are by default limited to the three static postures (standing, sitting, lying) plus three dynamic activities (walking, walking downstairs, walking upstairs).
The data is from a waist-mounted smartphone.
Samplerate is 50Hz.
-By default only the accelerometer data is used.
+By default only the accelerometer data is used (not the gyro).
+
+
+## Dataset 2: Excercise detection (custom)
-## TODO
+This dataset was collected using the data recording tools described further below.
+The data contains 3 kinds of exercises, plus "other" non-exercise activity.
+The classes are: Jumping Jacks, Squats, Lunges, Other.
+The data was collected using an M5Stick C PLUS 2, mounted on the wrist like a watch (button facing forward).
-- Add an illustrative image
-- Run the training + test/evaluation in CI
-- Add demonstration on LilyGo T-Watch 2020 (BMA423 accelerometer)
+
## Running on host
To run the example on your PC using Unix port of MicroPython.
-Make sure to have the Unix port of MicroPython 1.23 setup.
+Make sure to have the Unix port of MicroPython setup.
On Windows you can use Windows Subsystem for Linux (WSL), or Docker.
Download the files in this example directory
@@ -64,9 +75,11 @@ micropython -m mip install https://emlearn.github.io/emlearn-micropython/builds/
micropython har_run.py
```
+Results should be around 85% accuracy.
+
## Running on device (Viper IDE)
-Make sure to have MicroPython 1.23 installed on device.
+Make sure to have MicroPython installed on device.
The fastest and easiest to to install on your device is to use Viper IDE.
This will install the library and the example code:
@@ -76,7 +89,7 @@ This will install the library and the example code:
## Run on device
-Make sure to have MicroPython 1.23 installed on device.
+Make sure to have MicroPython installed on device.
Install the dependencies
```console
@@ -87,28 +100,54 @@ mpremote mip install github:jonnor/micropython-zipfile
Copy example code
```
-mpremote cp har_uci_trees.csv har_uci.testdata.npz timebased.py:
+mpremote cp har_uci_trees.csv har_uci.testdata.npz timebased.py :
```
Run model evaluation on a test set
```
-micropython har_run.py
+mpremote run har_run.py
```
-## Run on device (with live accelerometer data)
+Results should be around 85% accuracy.
-FIXME: document
+## Run on device (with live accelerometer data)
Requires a M5StickC PLUS 2.
Using a MPU6886 accelerometer connected via I2C.
-`har_live.py`
+Install dependencies. In addition to the above
+```
+mpremote mip install github:jonnor/micropython-mpu6886
+mpremote cp windower.py :
+```
+
+Run the classification
+```
+mpremote har_live.py
+```
+
+This will continiously output the results of the classification,
+and count the time spent in each class.
+
+```
+classify other [0.0, 0.0, 1.0, 0.0] 216 ms
+
+jumpingjack: 6 seconds
+lunge: 0 seconds
+other: 34 seconds
+squat: 0 seconds
+
+ble-advertise mac=10061c172302 data=aa0100050000ff00
+```
+The device also sends the classifications out as Bluetooth Low Energy advertisements.
+See the code for how the data is encoded used.
+This cata can be received on a smartphone or computer, for tracking and storage.
## Run training
-This will train a new model.
-Uses CPython on the PC.
+This will train a new model for the HAR UCI dataset.
+You need to have Python (CPython) installed on the PC.
Install requirements
```
@@ -128,4 +167,92 @@ python har_train.py
This will output a new model (named `_trees.csv`).
Can then be deployed to device following the steps above.
+## Recording motion data for custom tasks
+
+To learn a classifier for your own custom tasks, you will need to: 1) record data, 2) label the data, 3) run training with custom data.
+This example provides some basic tools to assist with this process.
+
+Recording data. Requires a M5StickC PLUS 2.
+Before you do this, make sure to **first run live classification example** (to get the dependencies).
+
+Copy additional dependencies
+```
+mpremote cp recorder.py :
+```
+
+
+Run the recording program
+```
+mpremote run har_record.py
+```
+
+Alternatively: Copy the program to device.
+This way it will run even if there is no USB device connected.
+```
+mpremote cp har_record.py main.py
+mpremote reset
+```
+
+To get good timestamps on the recorded files, rembember to set the RTC clock.
+```
+mpremote rtc --set
+```
+
+When the recording program runs, the device should show a screen which allows to select between different classes.
+Clicking the big button by the screen allows selecting class.
+Holding the big button down allows to start recording.
+The red LED will light up while recording.
+
+This allows to coarsely label the classes, which is helpful to sort and annotate them later.
+To adjust the classes, make changes to `har_record.py`.
+
+The files are placed in the `data/` folder on the internal FLASH filesystem.
+
+To copy the files over to your computer, for building a dataset, use
+```
+mkdir ./data/raw/mydata1/
+mpremote cp -r :./har_record ./data/raw/mydata1/
+```
+
+## Labeling recorded motion data
+
+To label the motion data, we can use [Label Studio](https://labelstud.io/).
+You can use their hosted version, or run it in Docker, or install it via `pip`.
+
+There is a ready-made Label Studio task defintion provided,
+in the file `labeling/har_classify_config.xml`.
+
+There is a tool designed to convert the .npy files from the `har_record.py` application
+into .csv files understood by Label Studio.
+
+```
+python har_data2labelstudio.py
+```
+
+When you have started Label Studio, create a new Project, and Import the .CSV files as data.
+Then you can label each piece of data.
+
+
+Then use this tool to combine the sensor data files with the labels.
+```
+python har_labelstudio2dataset.py
+```
+
+This will produce a dataset as a .parquet file, which can be used with `har_train.py`.
+
+
+## Train model on custom dataset
+
+
+Add your dataset definition to `dataset_config` in `har_train.py`, with a unique name (example `mydata1`).
+
+Then you can run the training process.
+
+```
+python har_train.py --dataset mydata1
+```
+
+It should output a new model (named `_trees.csv`).
+This model can be deployed to device following the steps above.
+
diff --git a/examples/har_trees/color_setup.py b/examples/har_trees/color_setup.py
new file mode 100644
index 0000000..9d74d0a
--- /dev/null
+++ b/examples/har_trees/color_setup.py
@@ -0,0 +1,23 @@
+
+import gc
+from machine import Pin, SPI
+
+# M5Stack M5StickC PLUS 2
+# https://docs.m5stack.com/en/core/M5StickC%20PLUS2
+from drivers.st7789.st7789_4bit import *
+# ESP32 GPIO15 GPIO13 GPIO14 GPIO12 GPIO5 GPIO27
+# TFT LCD TFT_MOSI TFT_CLK TFT_DC TFT_RST TFT_CS TFT_BL
+
+pdc = Pin(14, Pin.OUT, value=0)
+pcs = Pin(5, Pin.OUT, value=1)
+prst = Pin(12, Pin.OUT, value=1)
+pbl = Pin(27, Pin.OUT, value=1)
+
+gc.collect() # Precaution before instantiating framebuf
+
+SSD = ST7789
+
+# Conservative low baudrate. Can go to 62.5MHz.
+spi = SPI(1, 30_000_000, sck=Pin(13), mosi=Pin(15))
+ssd = ST7789(spi, height=135, width=240, dc=pdc, cs=pcs, rst=prst, disp_mode=LANDSCAPE, display=TDISPLAY)
+
diff --git a/examples/har_trees/compute_features.py b/examples/har_trees/compute_features.py
index 125ab92..23f23a9 100644
--- a/examples/har_trees/compute_features.py
+++ b/examples/har_trees/compute_features.py
@@ -5,7 +5,7 @@
import npyfile
-from timebased import calculate_features_xyz, DATA_TYPECODE
+from timebased import calculate_features_xyz, DATA_TYPECODE, N_FEATURES
def compute_dataset_features(data: npyfile.Reader,
skip_samples=0, limit_samples=None, verbose=0):
@@ -15,7 +15,7 @@ def compute_dataset_features(data: npyfile.Reader,
assert len(shape) == 3, shape
n_samples, window_length, n_axes = shape
assert n_axes == 3, shape
- assert window_length == 128, shape
+ #assert window_length == 128, shape
# We expect data to be h/int16
assert data.typecode == DATA_TYPECODE, data.typecode
@@ -70,7 +70,7 @@ def main():
limit_samples = None
out_typecode = 'f'
- n_features = 92
+ n_features = N_FEATURES
features_array = array.array(out_typecode, (0 for _ in range(n_features)))
diff --git a/examples/har_trees/data/processed/har_exercise_1.parquet b/examples/har_trees/data/processed/har_exercise_1.parquet
new file mode 100644
index 0000000..4311761
Binary files /dev/null and b/examples/har_trees/data/processed/har_exercise_1.parquet differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:00:51_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:00:51_jumpingjack.npy
new file mode 100644
index 0000000..754ca5d
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:00:51_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:01:32_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:01:32_other.npy
new file mode 100644
index 0000000..1872d23
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:01:32_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:01:42_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:01:42_other.npy
new file mode 100644
index 0000000..f5d53f0
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:01:42_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:01:52_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:01:52_other.npy
new file mode 100644
index 0000000..54dd900
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:01:52_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:02_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:02_other.npy
new file mode 100644
index 0000000..231a494
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:02_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:04_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:04_lunge.npy
new file mode 100644
index 0000000..dc3c966
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:04_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:11_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:11_other.npy
new file mode 100644
index 0000000..12bdb5a
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:11_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:14_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:14_lunge.npy
new file mode 100644
index 0000000..49a89be
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:14_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:21_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:21_other.npy
new file mode 100644
index 0000000..9e1b21c
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:21_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:24_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:24_lunge.npy
new file mode 100644
index 0000000..879bd0f
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:24_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:31_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:31_other.npy
new file mode 100644
index 0000000..5e74857
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:31_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:33_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:33_lunge.npy
new file mode 100644
index 0000000..0523dd4
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:33_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:43_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:43_lunge.npy
new file mode 100644
index 0000000..8bfd615
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:43_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:43_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:43_other.npy
new file mode 100644
index 0000000..35fe771
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:43_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:52_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:52_other.npy
new file mode 100644
index 0000000..c50f992
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:52_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:53_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:53_lunge.npy
new file mode 100644
index 0000000..19180c9
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:02:53_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:02_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:02_other.npy
new file mode 100644
index 0000000..3d9fb87
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:02_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:08_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:08_squat.npy
new file mode 100644
index 0000000..c232bfc
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:08_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:17_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:17_squat.npy
new file mode 100644
index 0000000..5652a4d
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:17_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:22_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:22_jumpingjack.npy
new file mode 100644
index 0000000..28ac98a
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:22_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:27_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:27_squat.npy
new file mode 100644
index 0000000..3adca36
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:27_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:31_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:31_jumpingjack.npy
new file mode 100644
index 0000000..8b96569
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:31_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:37_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:37_squat.npy
new file mode 100644
index 0000000..af691b1
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:37_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:41_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:41_jumpingjack.npy
new file mode 100644
index 0000000..eaed551
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:41_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:47_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:47_squat.npy
new file mode 100644
index 0000000..f02af85
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:47_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:51_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:51_jumpingjack.npy
new file mode 100644
index 0000000..435a7f6
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:51_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:57_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:57_squat.npy
new file mode 100644
index 0000000..333dd50
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:03:57_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:04:01_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:04:01_jumpingjack.npy
new file mode 100644
index 0000000..a8ae541
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:04:01_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:04:07_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2000-01-01T00:04:07_squat.npy
new file mode 100644
index 0000000..e69de29
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:07_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:07_other.npy
new file mode 100644
index 0000000..aca0596
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:07_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:17_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:17_other.npy
new file mode 100644
index 0000000..6388d34
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:17_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:27_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:27_other.npy
new file mode 100644
index 0000000..fdc76ca
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:27_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:37_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:37_other.npy
new file mode 100644
index 0000000..27a6024
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:37_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:47_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:47_other.npy
new file mode 100644
index 0000000..2fbf4fe
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:47_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:57_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:57_other.npy
new file mode 100644
index 0000000..20c6e9e
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:36:57_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:06_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:06_other.npy
new file mode 100644
index 0000000..40e60be
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:06_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:19_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:19_other.npy
new file mode 100644
index 0000000..2754999
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:19_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:29_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:29_other.npy
new file mode 100644
index 0000000..e835f67
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:29_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:38_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:38_other.npy
new file mode 100644
index 0000000..0e296c1
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:38_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:48_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:48_other.npy
new file mode 100644
index 0000000..7f7d1f5
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:48_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:58_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:58_other.npy
new file mode 100644
index 0000000..d6bd25f
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:37:58_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:08_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:08_other.npy
new file mode 100644
index 0000000..f40a373
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:08_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:18_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:18_other.npy
new file mode 100644
index 0000000..d1655fe
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:18_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:28_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:28_other.npy
new file mode 100644
index 0000000..b91691d
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:28_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:38_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:38_other.npy
new file mode 100644
index 0000000..31b4c1e
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:38_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:48_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:48_other.npy
new file mode 100644
index 0000000..30ab865
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:48_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:58_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:58_other.npy
new file mode 100644
index 0000000..c6bf137
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:38:58_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:08_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:08_other.npy
new file mode 100644
index 0000000..76af5b7
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:08_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:18_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:18_other.npy
new file mode 100644
index 0000000..2e06e03
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:18_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:27_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:27_other.npy
new file mode 100644
index 0000000..d747d28
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:27_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:37_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:37_other.npy
new file mode 100644
index 0000000..885aca6
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:37_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:59_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:59_other.npy
new file mode 100644
index 0000000..d335d89
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:39:59_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:09_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:09_other.npy
new file mode 100644
index 0000000..8e2ec29
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:09_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:19_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:19_other.npy
new file mode 100644
index 0000000..c40a6c7
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:19_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:29_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:29_other.npy
new file mode 100644
index 0000000..c3abce0
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:29_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:39_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:39_other.npy
new file mode 100644
index 0000000..6fe46ef
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:39_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:49_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:49_other.npy
new file mode 100644
index 0000000..6dea3ae
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:49_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:58_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:58_other.npy
new file mode 100644
index 0000000..8b9343c
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:40:58_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:08_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:08_other.npy
new file mode 100644
index 0000000..5207968
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:08_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:18_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:18_other.npy
new file mode 100644
index 0000000..49a50ae
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:18_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:28_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:28_other.npy
new file mode 100644
index 0000000..ed73c46
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:28_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:38_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:38_other.npy
new file mode 100644
index 0000000..dbd4d46
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:38_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:48_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:48_other.npy
new file mode 100644
index 0000000..fa16c49
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:48_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:58_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:58_other.npy
new file mode 100644
index 0000000..cb380b7
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:41:58_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:08_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:08_other.npy
new file mode 100644
index 0000000..4c1036d
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:08_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:28_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:28_other.npy
new file mode 100644
index 0000000..414e9cc
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:28_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:38_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:38_other.npy
new file mode 100644
index 0000000..ff37064
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:38_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:47_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:47_other.npy
new file mode 100644
index 0000000..ce4af3f
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:47_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:57_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:57_other.npy
new file mode 100644
index 0000000..d86611f
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:42:57_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:07_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:07_other.npy
new file mode 100644
index 0000000..cedfd63
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:07_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:17_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:17_other.npy
new file mode 100644
index 0000000..ad9b927
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:17_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:27_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:27_other.npy
new file mode 100644
index 0000000..4889a2a
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:27_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:37_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:37_other.npy
new file mode 100644
index 0000000..ae714e5
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:37_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:47_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:47_other.npy
new file mode 100644
index 0000000..20307ac
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:47_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:57_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:57_other.npy
new file mode 100644
index 0000000..1a2095a
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:43:57_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:07_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:07_other.npy
new file mode 100644
index 0000000..4f6b9ee
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:07_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:17_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:17_other.npy
new file mode 100644
index 0000000..0a1ff04
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:17_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:27_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:27_other.npy
new file mode 100644
index 0000000..1d6b03f
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:27_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:36_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:36_other.npy
new file mode 100644
index 0000000..8122887
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:36_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:46_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:46_other.npy
new file mode 100644
index 0000000..c25150e
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T15:44:46_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:05_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:05_squat.npy
new file mode 100644
index 0000000..6ed55d0
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:05_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:15_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:15_squat.npy
new file mode 100644
index 0000000..c04e077
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:15_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:25_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:25_squat.npy
new file mode 100644
index 0000000..97dcbd4
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:25_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:35_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:35_squat.npy
new file mode 100644
index 0000000..426c579
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:35_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:45_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:45_squat.npy
new file mode 100644
index 0000000..675b68d
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:45_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:55_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:55_squat.npy
new file mode 100644
index 0000000..df4ede4
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:04:55_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:04_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:04_squat.npy
new file mode 100644
index 0000000..97634d1
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:04_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:14_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:14_squat.npy
new file mode 100644
index 0000000..77f7e99
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:14_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:35_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:35_jumpingjack.npy
new file mode 100644
index 0000000..a2bc9d5
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:35_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:45_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:45_jumpingjack.npy
new file mode 100644
index 0000000..4ed8c1d
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:45_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:54_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:54_jumpingjack.npy
new file mode 100644
index 0000000..1c35e4e
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:05:54_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:04_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:04_jumpingjack.npy
new file mode 100644
index 0000000..5de7d39
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:04_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:14_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:14_jumpingjack.npy
new file mode 100644
index 0000000..796981a
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:14_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:24_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:24_jumpingjack.npy
new file mode 100644
index 0000000..27e9f20
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:24_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:56_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:56_lunge.npy
new file mode 100644
index 0000000..4acc260
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:06:56_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:06_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:06_lunge.npy
new file mode 100644
index 0000000..bbab25a
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:06_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:16_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:16_lunge.npy
new file mode 100644
index 0000000..e40dc59
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:16_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:26_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:26_lunge.npy
new file mode 100644
index 0000000..9b2bc57
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:26_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:37_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:37_lunge.npy
new file mode 100644
index 0000000..ea0e853
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:37_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:47_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:47_lunge.npy
new file mode 100644
index 0000000..4a267ae
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:47_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:57_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:57_lunge.npy
new file mode 100644
index 0000000..f6f7216
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:07:57_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:08:07_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:08:07_lunge.npy
new file mode 100644
index 0000000..7a2cea0
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:08:07_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:06_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:06_other.npy
new file mode 100644
index 0000000..c51c96f
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:06_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:16_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:16_other.npy
new file mode 100644
index 0000000..5c14903
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:16_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:26_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:26_other.npy
new file mode 100644
index 0000000..52bb20d
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:26_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:35_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:35_other.npy
new file mode 100644
index 0000000..54e2d63
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:35_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:45_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:45_other.npy
new file mode 100644
index 0000000..6182bd7
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:45_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:55_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:55_other.npy
new file mode 100644
index 0000000..8c05236
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:21:55_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:05_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:05_other.npy
new file mode 100644
index 0000000..09ce15b
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:05_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:15_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:15_other.npy
new file mode 100644
index 0000000..15f2d45
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:15_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:25_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:25_other.npy
new file mode 100644
index 0000000..3394758
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:25_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:35_other.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:35_other.npy
new file mode 100644
index 0000000..7b1c167
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:22:35_other.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:07_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:07_jumpingjack.npy
new file mode 100644
index 0000000..b21a168
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:07_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:17_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:17_jumpingjack.npy
new file mode 100644
index 0000000..63a02aa
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:17_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:27_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:27_jumpingjack.npy
new file mode 100644
index 0000000..49b0906
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:27_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:37_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:37_jumpingjack.npy
new file mode 100644
index 0000000..5306027
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:37_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:48_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:48_jumpingjack.npy
new file mode 100644
index 0000000..8653fd7
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:48_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:58_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:58_jumpingjack.npy
new file mode 100644
index 0000000..186f72a
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:23:58_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:08_jumpingjack.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:08_jumpingjack.npy
new file mode 100644
index 0000000..676f243
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:08_jumpingjack.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:28_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:28_lunge.npy
new file mode 100644
index 0000000..462e6db
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:28_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:38_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:38_lunge.npy
new file mode 100644
index 0000000..3bc5715
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:38_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:48_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:48_lunge.npy
new file mode 100644
index 0000000..a113eb3
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:48_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:58_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:58_lunge.npy
new file mode 100644
index 0000000..de20bf0
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:24:58_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:08_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:08_lunge.npy
new file mode 100644
index 0000000..177b098
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:08_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:18_lunge.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:18_lunge.npy
new file mode 100644
index 0000000..0b71411
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:18_lunge.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:31_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:31_squat.npy
new file mode 100644
index 0000000..68e55f3
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:31_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:41_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:41_squat.npy
new file mode 100644
index 0000000..35dcf37
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:41_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:51_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:51_squat.npy
new file mode 100644
index 0000000..8a39d28
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:25:51_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:01_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:01_squat.npy
new file mode 100644
index 0000000..7baa75d
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:01_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:10_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:10_squat.npy
new file mode 100644
index 0000000..41c5e70
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:10_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:20_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:20_squat.npy
new file mode 100644
index 0000000..1e61d2f
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:20_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:30_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:30_squat.npy
new file mode 100644
index 0000000..af45ba8
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:30_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:40_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:40_squat.npy
new file mode 100644
index 0000000..9e55236
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:40_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:50_squat.npy b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:50_squat.npy
new file mode 100644
index 0000000..43469bb
Binary files /dev/null and b/examples/har_trees/data/raw/har_record_exercises_1/data/2024-12-01T16:26:50_squat.npy differ
diff --git a/examples/har_trees/data/raw/har_record_exercises_1/labels/project-3-at-2024-12-01-17-29-ba296417.csv b/examples/har_trees/data/raw/har_record_exercises_1/labels/project-3-at-2024-12-01-17-29-ba296417.csv
new file mode 100644
index 0000000..d158a56
--- /dev/null
+++ b/examples/har_trees/data/raw/har_record_exercises_1/labels/project-3-at-2024-12-01-17-29-ba296417.csv
@@ -0,0 +1,67 @@
+"annotation_id","annotator","created_at","id","lead_time","timeseriesUrl","trend_forecast","updated_at"
+37,"1","2024-12-01T16:46:00.606286Z",119,26.22,"/data/upload/3/f085fe84-2024-12-01T162620_squat.npy.csv","squat","2024-12-01T16:46:06.716751Z"
+38,"1","2024-12-01T16:46:18.050513Z",120,8.362,"/data/upload/3/65692c05-2024-12-01T162601_squat.npy.csv","other","2024-12-01T16:46:18.050532Z"
+39,"1","2024-12-01T16:46:21.964683Z",121,2.523,"/data/upload/3/b3b4314e-2024-12-01T162541_squat.npy.csv","squat","2024-12-01T16:46:21.964709Z"
+40,"1","2024-12-01T16:46:28.628280Z",122,5.581,"/data/upload/3/6e7d7b4f-2024-12-01T162458_lunge.npy.csv","lunge","2024-12-01T16:46:28.628299Z"
+41,"1","2024-12-01T16:46:33.029374Z",123,2.711,"/data/upload/3/772dafa4-2024-12-01T162448_lunge.npy.csv","lunge","2024-12-01T16:46:33.029401Z"
+42,"1","2024-12-01T16:46:38.365943Z",124,3.872,"/data/upload/3/f80d506c-2024-12-01T162428_lunge.npy.csv","mixed","2024-12-01T16:46:38.365966Z"
+43,"1","2024-12-01T16:46:45.098587Z",125,5.727,"/data/upload/3/383846d0-2024-12-01T162225_other.npy.csv","other","2024-12-01T16:46:45.098607Z"
+44,"1","2024-12-01T16:46:48.752012Z",126,2.514,"/data/upload/3/e0525ac5-2024-12-01T162205_other.npy.csv","other","2024-12-01T16:46:48.752066Z"
+45,"1","2024-12-01T16:46:51.942826Z",127,2.214,"/data/upload/3/6cd64f5b-2024-12-01T162155_other.npy.csv","other","2024-12-01T16:46:51.942846Z"
+46,"1","2024-12-01T16:46:55.216257Z",128,2.317,"/data/upload/3/21d0dc28-2024-12-01T162145_other.npy.csv","other","2024-12-01T16:46:55.216281Z"
+53,"1","2024-12-01T16:48:32.432309Z",129,4.588,"/data/upload/3/39aec739-2024-12-01T162135_other.npy.csv","other","2024-12-01T16:48:32.432330Z"
+47,"1","2024-12-01T16:47:03.241658Z",130,6.328,"/data/upload/3/9036d4f9-2024-12-01T162640_squat.npy.csv","mixed","2024-12-01T16:47:03.241678Z"
+48,"1","2024-12-01T16:47:22.200593Z",131,18.086,"/data/upload/3/8ab535dd-2024-12-01T160747_lunge.npy.csv","lunge","2024-12-01T16:47:22.200622Z"
+49,"1","2024-12-01T16:47:30.308274Z",132,6.675,"/data/upload/3/abfa2d30-2024-12-01T160656_lunge.npy.csv","mixed","2024-12-01T16:47:30.308293Z"
+50,"1","2024-12-01T16:47:35.375734Z",133,4.116,"/data/upload/3/6e15b2da-2024-12-01T160554_jumpingjack.npy.csv","mixed","2024-12-01T16:47:35.375753Z"
+51,"1","2024-12-01T16:47:39.951597Z",134,3.23,"/data/upload/3/78b36a7b-2024-12-01T160545_jumpingjack.npy.csv","jumpingjack","2024-12-01T16:47:39.951621Z"
+52,"1","2024-12-01T16:47:51.026050Z",135,9.587,"/data/upload/3/9241dcd2-2024-12-01T160535_jumpingjack.npy.csv","mixed","2024-12-01T16:47:51.026070Z"
+54,"1","2024-12-01T16:48:40.099719Z",136,5.624,"/data/upload/3/5fabab61-2024-12-01T154436_other.npy.csv","other","2024-12-01T16:48:40.099740Z"
+55,"1","2024-12-01T16:48:48.716069Z",137,7.652,"/data/upload/3/5b190777-2024-12-01T160445_squat.npy.csv","squat","2024-12-01T16:48:48.716087Z"
+56,"1","2024-12-01T16:48:52.446110Z",138,2.795,"/data/upload/3/26895628-2024-12-01T162155_other.npy.csv","other","2024-12-01T16:48:52.446134Z"
+57,"1","2024-12-01T16:48:57.097079Z",139,3.735,"/data/upload/3/b7271bf9-2024-12-01T160415_squat.npy.csv","squat","2024-12-01T16:48:57.097114Z"
+58,"1","2024-12-01T16:49:00.618956Z",140,2.48,"/data/upload/3/2200fbb2-2024-12-01T162541_squat.npy.csv","squat","2024-12-01T16:49:00.618979Z"
+59,"1","2024-12-01T16:49:04.395530Z",141,2.815,"/data/upload/3/109dc313-2024-12-01T160425_squat.npy.csv","squat","2024-12-01T16:49:04.395550Z"
+60,"1","2024-12-01T16:49:10.500354Z",142,5.113,"/data/upload/3/d95c80a5-2024-12-01T154118_other.npy.csv","other","2024-12-01T16:49:10.500383Z"
+61,"1","2024-12-01T16:49:14.587652Z",143,3.228,"/data/upload/3/0a017766-2024-12-01T160504_squat.npy.csv","mixed","2024-12-01T16:49:14.587675Z"
+62,"1","2024-12-01T16:49:26.373650Z",144,10.973,"/data/upload/3/2d7c850a-2024-12-01T162640_squat.npy.csv","mixed","2024-12-01T16:49:26.373671Z"
+63,"1","2024-12-01T16:49:30.742817Z",145,3.429,"/data/upload/3/49d3bfb7-2024-12-01T153647_other.npy.csv","other","2024-12-01T16:49:30.742837Z"
+64,"1","2024-12-01T16:49:34.278092Z",146,2.578,"/data/upload/3/3a9e1ae5-2024-12-01T154427_other.npy.csv","other","2024-12-01T16:49:34.278116Z"
+65,"1","2024-12-01T16:49:39.583607Z",147,4.125,"/data/upload/3/5a685628-2024-12-01T153729_other.npy.csv","other","2024-12-01T16:49:39.583626Z"
+66,"1","2024-12-01T16:49:43.627384Z",148,3.088,"/data/upload/3/fa070afd-2024-12-01T154407_other.npy.csv","other","2024-12-01T16:49:43.627415Z"
+67,"1","2024-12-01T16:50:48.401960Z",149,6.505,"/data/upload/3/53943cc4-2024-12-01T153607_other.npy.csv","other","2024-12-01T16:50:48.401986Z"
+68,"1","2024-12-01T16:51:01.990778Z",151,6.135,"/data/upload/3/50cf0f1d-2024-12-01T160737_lunge.npy.csv","lunge","2024-12-01T16:51:01.990809Z"
+69,"1","2024-12-01T16:51:05.306586Z",152,2.195,"/data/upload/3/367bb9a4-2024-12-01T160757_lunge.npy.csv","lunge","2024-12-01T16:51:05.306612Z"
+70,"1","2024-12-01T16:51:11.683648Z",153,4.712,"/data/upload/3/71605682-2024-12-01T154327_other.npy.csv","other","2024-12-01T16:51:11.683667Z"
+71,"1","2024-12-01T16:51:15.973171Z",154,3.177,"/data/upload/3/52fad17a-2000-01-01T000214_lunge.npy.csv","lunge","2024-12-01T16:51:15.973192Z"
+72,"1","2024-12-01T16:51:27.162855Z",157,6.69,"/data/upload/3/4163278f-2000-01-01T000224_lunge.npy.csv","lunge","2024-12-01T16:51:27.162879Z"
+73,"1","2024-12-01T16:51:32.754956Z",158,4.423,"/data/upload/3/8000dc1d-2000-01-01T000308_squat.npy.csv","squat","2024-12-01T16:51:32.754985Z"
+74,"1","2024-12-01T16:51:39.035757Z",159,5.29,"/data/upload/3/ddc61c3b-2024-12-01T154158_other.npy.csv","other","2024-12-01T16:51:39.035776Z"
+75,"1","2024-12-01T16:51:58.891750Z",160,19.066,"/data/upload/3/c15c3ef8-2024-12-01T162126_other.npy.csv","other","2024-12-01T16:51:58.891769Z"
+76,"1","2024-12-01T16:52:07.626580Z",161,7.998,"/data/upload/3/0a8cb978-2024-12-01T162358_jumpingjack.npy.csv","other","2024-12-01T16:52:07.626607Z"
+77,"1","2024-12-01T16:52:11.398579Z",162,2.8,"/data/upload/3/12dfc7ba-2024-12-01T162438_lunge.npy.csv","lunge","2024-12-01T16:52:11.398597Z"
+78,"1","2024-12-01T16:52:15.846957Z",163,2.713,"/data/upload/3/d7b90799-2024-12-01T153637_other.npy.csv","other","2024-12-01T16:52:15.846984Z"
+79,"1","2024-12-01T16:52:20.272867Z",164,3.173,"/data/upload/3/d2e5c317-2024-12-01T162337_jumpingjack.npy.csv","jumpingjack","2024-12-01T16:52:20.272896Z"
+80,"1","2024-12-01T16:52:26.923186Z",165,5.682,"/data/upload/3/f3ab6f35-2000-01-01T000202_other.npy.csv","other","2024-12-01T16:52:26.923217Z"
+81,"1","2024-12-01T16:52:31.045193Z",166,3.298,"/data/upload/3/2d2c9388-2024-12-01T153959_other.npy.csv","other","2024-12-01T16:52:31.045212Z"
+82,"1","2024-12-01T16:52:34.531193Z",167,2.567,"/data/upload/3/92924cb1-2024-12-01T162215_other.npy.csv","other","2024-12-01T16:52:34.531211Z"
+83,"1","2024-12-01T16:52:37.678208Z",168,2.384,"/data/upload/3/62c2b23f-2024-12-01T154138_other.npy.csv","other","2024-12-01T16:52:37.678226Z"
+84,"1","2024-12-01T16:52:42.293066Z",169,3.108,"/data/upload/3/40249efb-2000-01-01T000331_jumpingjack.npy.csv","jumpingjack","2024-12-01T16:52:42.293093Z"
+85,"1","2024-12-01T16:52:45.500191Z",170,2.183,"/data/upload/3/d2e1321e-2000-01-01T000152_other.npy.csv","other","2024-12-01T16:52:45.500210Z"
+86,"1","2024-12-01T16:52:49.740688Z",171,3.5,"/data/upload/3/ff00da9c-2024-12-01T153719_other.npy.csv","other","2024-12-01T16:52:49.740716Z"
+87,"1","2024-12-01T16:52:53.340534Z",172,2.786,"/data/upload/3/28cb6011-2024-12-01T162610_squat.npy.csv","squat","2024-12-01T16:52:53.340559Z"
+88,"1","2024-12-01T16:52:59.725051Z",173,5.481,"/data/upload/3/fcea4a67-2024-12-01T153706_other.npy.csv","other","2024-12-01T16:52:59.725077Z"
+89,"1","2024-12-01T16:53:04.792915Z",174,4.181,"/data/upload/3/d42b8a71-2024-12-01T162348_jumpingjack.npy.csv","jumpingjack","2024-12-01T16:53:04.792936Z"
+90,"1","2024-12-01T16:53:14.331070Z",175,8.374,"/data/upload/3/31aa3f7d-2024-12-01T162531_squat.npy.csv","mixed","2024-12-01T16:53:14.331089Z"
+91,"1","2024-12-01T17:28:08.864603Z",176,3.427,"/data/upload/3/c042d297-2024-12-01T162317_jumpingjack.npy.csv","jumpingjack","2024-12-01T17:28:08.864625Z"
+92,"1","2024-12-01T17:28:13.319824Z",177,3.407,"/data/upload/3/cc8667c2-2024-12-01T160614_jumpingjack.npy.csv","mixed","2024-12-01T17:28:13.319844Z"
+93,"1","2024-12-01T17:28:16.523754Z",178,2.315,"/data/upload/3/22f3a67b-2000-01-01T000351_jumpingjack.npy.csv","jumpingjack","2024-12-01T17:28:16.523772Z"
+94,"1","2024-12-01T17:28:21.080026Z",179,3.56,"/data/upload/3/07cd4855-2000-01-01T000341_jumpingjack.npy.csv","mixed","2024-12-01T17:28:21.080045Z"
+95,"1","2024-12-01T17:28:24.774913Z",180,2.734,"/data/upload/3/b0fa2f9b-2024-12-01T160604_jumpingjack.npy.csv","jumpingjack","2024-12-01T17:28:24.774939Z"
+96,"1","2024-12-01T17:28:28.933966Z",181,3.283,"/data/upload/3/ccf5b3d4-2024-12-01T162307_jumpingjack.npy.csv","jumpingjack","2024-12-01T17:28:28.933992Z"
+97,"1","2024-12-01T17:28:38.600107Z",182,4.659,"/data/upload/3/df4d0c79-2024-12-01T162358_jumpingjack.npy.csv","other","2024-12-01T17:28:38.600131Z"
+98,"1","2024-12-01T17:28:43.129193Z",183,3.692,"/data/upload/3/e0c85e3d-2024-12-01T162337_jumpingjack.npy.csv","jumpingjack","2024-12-01T17:28:43.129220Z"
+99,"1","2024-12-01T17:28:47.214660Z",184,3.368,"/data/upload/3/9df378bd-2000-01-01T000331_jumpingjack.npy.csv","jumpingjack","2024-12-01T17:28:47.214690Z"
+100,"1","2024-12-01T17:28:51.571520Z",185,2.435,"/data/upload/3/6420cfff-2024-12-01T162348_jumpingjack.npy.csv","jumpingjack","2024-12-01T17:28:51.571540Z"
+101,"1","2024-12-01T17:28:56.128979Z",186,3.007,"/data/upload/3/b9074022-2024-12-01T162327_jumpingjack.npy.csv","other","2024-12-01T17:28:56.129007Z"
+102,"1","2024-12-01T17:28:59.185708Z",187,2.259,"/data/upload/3/289c5cba-2000-01-01T000322_jumpingjack.npy.csv","jumpingjack","2024-12-01T17:28:59.185728Z"
diff --git a/examples/har_trees/har_data2labelstudio.py b/examples/har_trees/har_data2labelstudio.py
new file mode 100644
index 0000000..52452ce
--- /dev/null
+++ b/examples/har_trees/har_data2labelstudio.py
@@ -0,0 +1,72 @@
+
+
+import os
+
+import pandas
+import numpy
+
+def load_har_record(path,
+ samplerate=100,
+ sensitivity=2.0,
+ maxvalue=32767,
+ ):
+
+ suffix = '.npy'
+
+ files = []
+
+ for f in os.listdir(path):
+ if f.endswith(suffix):
+ p = os.path.join(path, f)
+ try:
+ data = numpy.load(p, allow_pickle=True)
+ except Exception as e:
+ print(e)
+ continue
+
+ df = pandas.DataFrame(data, columns=['x', 'y', 'z'])
+
+ # Scale values into physical units (g)
+ df = df.astype(float) / maxvalue * sensitivity
+
+ # Add a time column, use as index
+ t = numpy.arange(0, len(df)) * (1.0/samplerate)
+ df['time'] = t
+ df = df.set_index('time')
+
+ classname = f.split('_')[1].rstrip(suffix)
+
+ # Remove :, special character on Windows
+ filename = f.replace(':', '')
+
+ files.append(dict(data=df, filename=filename, classname=classname))
+
+ #print(f, data.shape)
+
+ out = pandas.DataFrame.from_records(files)
+ out = out.set_index('filename')
+ return out
+
+def main():
+
+ p = './data/har_record_excercises/har_record'
+ data = load_har_record(p, samplerate=100)
+
+ print(data.head())
+ print(data.shape)
+ print(data.classname.value_counts())
+
+ out_dir = 'to_label'
+
+ # Write .CSV files, suitable for importing in Label Studio
+ for idx, f in data.iterrows():
+ d = f.data.sort_index()
+
+ out_path = os.path.join(out_dir, idx+'.csv')
+ d.to_csv(out_path)
+ print('Wrote', out_path)
+
+if __name__ == '__main__':
+ main()
+
+
diff --git a/examples/har_trees/har_labelstudio2dataset.py b/examples/har_trees/har_labelstudio2dataset.py
new file mode 100644
index 0000000..b51a678
--- /dev/null
+++ b/examples/har_trees/har_labelstudio2dataset.py
@@ -0,0 +1,103 @@
+
+import re
+import os
+
+import pandas
+
+
+
+from read_data import load_har_record
+
+def extract_filename(url):
+
+ base = os.path.basename(url)
+
+ # label studio adds a ID- on import
+ filename = re.sub(r'(\w+)-', '', base, count=1)
+
+ return filename
+
+def read_labels(path):
+ df = pandas.read_csv(path)
+
+ df = df.rename(columns={
+ 'trend_forecast': 'activity',
+ 'timeseriesUrl': 'data_url',
+ }, errors='ignore')
+
+ # Extract data identifier, used for correlating with data
+ df['file'] = df['data_url'].apply(extract_filename)
+
+ columns = ['file', 'activity']
+ df = df[columns]
+
+ # Convert to a single label per file
+ # even though there may be multiple annotations
+ df = df.groupby('file').agg(pandas.Series.mode)
+
+ #print(df.head())
+
+ return df
+
+
+
+def main():
+
+ out_path = 'har_exercise_1.parquet'
+ labels_path = 'project-3-at-2024-12-01-17-29-ba296417.csv'
+ data_path = 'data/har_record_excercises/har_record'
+ seed = 1
+
+ labels = read_labels(labels_path)
+
+ # Drop files with a mix of class labels
+ labels = labels[~labels.activity.isin(['mixed'])]
+
+ # Balance the 'other' category, by downsampling
+ without_other = labels[labels.activity != 'other']
+ class_occurance = int(without_other.value_counts().median())
+ only_other = labels[labels.activity == 'other']
+ other_downsampled = only_other.sample(n=class_occurance, random_state=seed)
+ labels = pandas.concat([without_other, other_downsampled])
+
+ print('\nFiles after balancing:')
+ print(labels.activity.value_counts())
+
+ # Lookup data
+ data = load_har_record(data_path)
+
+ # Merge in label data
+ dfs = []
+ for filename, row in labels.iterrows():
+ classname = row.activity
+ filename = filename.rstrip('.csv')
+ #print(filename, classname)
+
+ try:
+ d = data.loc[filename].data
+ except KeyError as e:
+ print('Load error', e)
+ continue
+
+ d = d.reset_index()
+ d['subject'] = 'unknown'
+ d['file'] = filename
+ d['activity'] = classname
+ dfs.append(d)
+
+ #p = 'data/processed/pamap2.parquet'
+
+ out = pandas.concat(dfs, ignore_index=True)
+ print(out)
+ out.to_parquet(out_path)
+
+ #return
+ # Sanity check
+ df = pandas.read_parquet(out_path)
+ print(df.columns)
+ print(df.activity.value_counts())
+ print(df.file.value_counts())
+ print(df.head())
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/har_trees/har_live.py b/examples/har_trees/har_live.py
index 3d8ab5d..7bb30c8 100644
--- a/examples/har_trees/har_live.py
+++ b/examples/har_trees/har_live.py
@@ -1,6 +1,8 @@
+import machine
from machine import Pin, I2C
from mpu6886 import MPU6886
+import bluetooth
import time
import struct
@@ -10,29 +12,169 @@
import timebased
import emlearn_trees
+# for display
+# mpremote mip install "github:peterhinch/micropython-nano-gui"
+from color_setup import ssd
+from gui.core.writer import Writer
+from gui.core.nanogui import refresh
+from gui.widgets.meter import Meter
+from gui.widgets.label import Label, ALIGN_RIGHT
+import gui.fonts.courier20 as fixed_font
+
+# Cleanup after import frees considerable memory
+gc.collect()
+
def mean(arr):
m = sum(arr) / float(len(arr))
return m
+def argmax(arr):
+ idx_max = 0
+ value_max = arr[0]
+ for i in range(1, len(arr)):
+ if arr[i] > value_max:
+ value_max = arr[i]
+ idx_max = i
+
+ return idx_max
+
+def copy_array_into(source, target):
+ assert len(source) == len(target)
+ for i in range(len(target)):
+ target[i] = source[i]
+
+def clamp(value, lower, upper) -> float:
+ v = value
+ v = min(v, upper)
+ v = max(v, lower)
+ return v
+
+def manufacturer_specific_advertisement(data : bytearray, manufacturer=[0xca, 0xab], limited_disc=False, br_edr=False):
+ _ADV_TYPE_FLAGS = const(0x01)
+ _ADV_TYPE_CUSTOMDATA = const(0xff)
+ _ADV_MAX_PAYLOAD = const(31)
+
+ payload = bytearray()
+
+ # Advertising payloads are repeated packets of the following form:
+ # 1 byte data length (N + 1)
+ # 1 byte type (see constants below)
+ # N bytes type-specific data
+ def _append(adv_type, value):
+ nonlocal payload
+ payload += struct.pack("BB", len(value) + 1, adv_type) + value
+
+ # Flags
+ _append(
+ _ADV_TYPE_FLAGS,
+ struct.pack("B", (0x01 if limited_disc else 0x02) + (0x18 if br_edr else 0x04)),
+ )
+
+ # Specify manufacturer-specific data
+ manufacturer_id = bytearray(manufacturer)
+ _append(_ADV_TYPE_CUSTOMDATA, (manufacturer_id + data))
+
+ if len(payload) > _ADV_MAX_PAYLOAD:
+ raise ValueError("advertising payload too large")
+
+ return payload
+
+def send_bluetooth_le(sequence, probabilities,
+ advertisements=4,
+ advertise_interval_ms=50,
+ format=0xAA,
+ version=0x01):
+ """
+ Send data as BLE advertisements
+ Delivery of advertisements are not guaranteed. So we repeat N times to have a decent chance
+ """
+
+ # Start BLE
+ ble = bluetooth.BLE()
+ ble.active(True)
+ mac = ble.config('mac')
+
+ # Encode data as BLE advertisement. Max 29 bytes
+ data = bytearray()
+ data += struct.pack('B', format)
+ data += struct.pack('B', version)
+ data += struct.pack('>H', sequence)
+
+ for p in probabilities:
+ q = int(clamp(p*255, 0, 255))
+ data += struct.pack('B', q)
+
+ payload = manufacturer_specific_advertisement(data)
+
+ print('ble-advertise', 'mac='+mac[1].hex(), 'data='+data.hex())
+
+ # send and wait until N advertisements are sent
+ advertise_us = int(1000*advertise_interval_ms)
+ ble.gap_advertise(advertise_us, adv_data=payload, connectable=False)
+ # XXX: blocking wait
+ time.sleep_ms(advertisements*advertise_interval_ms)
+
+ # Turn of BLE
+ ble.active(False)
+
+
+def render_display(durations):
+ start_time = time.ticks_ms()
+
+ ssd.fill(0)
+
+ Writer.set_textpos(ssd, 0, 0) # In case previous tests have altered it
+ wri = Writer(ssd, fixed_font, verbose=False)
+ wri.set_clip(False, False, False)
+
+ y = 5
+ for classname, stats in durations.items():
+
+ key_text = classname
+ text1 = Label(wri, y, 10, wri.stringlen(key_text))
+ text1.value(key_text)
+
+ value_text = f'{stats:.0f}s'
+ text2 = Label(wri, y, 140, 50, align=ALIGN_RIGHT)
+ text2.value(value_text)
+ y += 22
+
+ refresh(ssd)
+
+ duration = time.ticks_ms() - start_time
+ print('render-display', duration, 'ms')
+
+
def main():
- classname_index = {"LAYING": 0, "SITTING": 1, "STANDING": 2, "WALKING": 3, "WALKING_DOWNSTAIRS": 4, "WALKING_UPSTAIRS": 5}
+ dataset = 'har_exercise_1'
+
+ if dataset == 'uci_har':
+ classname_index = {"LAYING": 0, "SITTING": 1, "STANDING": 2, "WALKING": 3, "WALKING_DOWNSTAIRS": 4, "WALKING_UPSTAIRS": 5}
+ window_length = 128
+ elif dataset == 'har_exercise_1':
+ classname_index = {"jacks": 0, "lunge": 1, "other": 2, "squat": 3}
+ window_length = 256
+ else:
+ raise ValueError('Unknown dataset')
+
+ model_path = f'{dataset}_trees.csv'
class_index_to_name = { v: k for k, v in classname_index.items() }
+
# Load a CSV file with the model
model = emlearn_trees.new(10, 1000, 10)
- with open('har_uci_trees.csv', 'r') as f:
+ with open(model_path, 'r') as f:
emlearn_trees.load_model(model, f)
- i2c = I2C(sda=21, scl=22, freq=100000)
- mpu = MPU6886(i2c)
+ mpu = MPU6886(I2C(0, sda=21, scl=22, freq=100000))
# Enable FIFO at a fixed samplerate
+ SAMPLERATE = 100
mpu.fifo_enable(True)
- mpu.set_odr(100)
+ mpu.set_odr(SAMPLERATE)
- window_length = 100
- hop_length = 50
+ hop_length = 64
chunk = bytearray(mpu.bytes_per_sample*hop_length)
x_values = empty_array('h', hop_length)
@@ -40,9 +182,18 @@ def main():
z_values = empty_array('h', hop_length)
windower = TriaxialWindower(window_length)
+ x_window = empty_array('h', window_length)
+ y_window = empty_array('h', window_length)
+ z_window = empty_array('h', window_length)
+
features_typecode = timebased.DATA_TYPECODE
n_features = timebased.N_FEATURES
features = array.array(features_typecode, (0 for _ in range(n_features)))
+ out = array.array('f', range(model.outputs()))
+
+ prediction_no = 0
+ durations = { classname: 0.0 for classname in classname_index.keys() } # how long each class has been active
+ MIN_PROBABILITY = 0.5 # if no class has higher, consider as "other"
while True:
@@ -56,18 +207,41 @@ def main():
if windower.full():
# compute features
#print('xyz', mean(x_values), mean(y_values), mean(z_values))
- ff = timebased.calculate_features_xyz((x_values, y_values, z_values))
+
+ copy_array_into(windower.x_values, x_window)
+ copy_array_into(windower.y_values, y_window)
+ copy_array_into(windower.z_values, z_window)
+
+ ff = timebased.calculate_features_xyz((x_window, y_window, z_window))
for i, f in enumerate(ff):
features[i] = int(f)
# Cun classifier
- result = model.predict(features)
+ #print(features)
+ model.predict(features, out)
+ result = argmax(out)
activity = class_index_to_name[result]
+ if max(out) < MIN_PROBABILITY:
+ activity = 'other'
+
+ durations[activity] += (hop_length/SAMPLERATE)
+ # Print status
d = time.ticks_diff(time.ticks_ms(), start)
- print('class', activity, d)
+ print('classify', activity, list(out), d, 'ms')
+ for classname, duration in durations.items():
+ print(f'{classname}:\t\t\t{duration:.0f} seconds')
+
+ # Send predictions over BLE
+ send_bluetooth_le(prediction_no, out)
+
+ # Update display
+ render_display(durations)
+
+ prediction_no += 1
- time.sleep_ms(1)
+ time.sleep_ms(100)
+ #machine.lightsleep(100)
if __name__ == '__main__':
diff --git a/examples/har_trees/har_record.py b/examples/har_trees/har_record.py
new file mode 100644
index 0000000..460158a
--- /dev/null
+++ b/examples/har_trees/har_record.py
@@ -0,0 +1,176 @@
+
+
+import machine
+from machine import Pin, I2C
+
+from mpu6886 import MPU6886
+
+# mpremote mip install "github:peterhinch/micropython-async/v3/primitives"
+from primitives import Pushbutton
+
+import asyncio
+import os
+import time
+import array
+import struct
+import gc
+
+from recorder import Recorder
+
+# for display
+# mpremote mip install "github:peterhinch/micropython-nano-gui"
+from color_setup import ssd
+from gui.core.writer import Writer
+from gui.core.nanogui import refresh
+from gui.widgets.meter import Meter
+from gui.widgets.label import Label
+import gui.fonts.courier20 as fixed
+
+# Cleanup after import frees considerable memory
+gc.collect()
+
+def decode_samples(buf : bytearray, samples : array.array, bytes_per_sample):
+ """
+ Convert raw bytes into int16 arrays
+ """
+ assert (len(buf) % bytes_per_sample) == 0
+ n_samples = len(buf) // bytes_per_sample
+ assert len(samples) == 3*n_samples, (len(samples), 3*n_samples)
+
+ #view = memoryview(buf)
+ for i in range(n_samples):
+ # NOTE: temperature (follows z) is ignored
+ x, y, z = struct.unpack_from('>hhh', buf, i*bytes_per_sample)
+ samples[(i*3)+0] = x
+ samples[(i*3)+1] = y
+ samples[(i*3)+2] = z
+
+
+def render_display(selected_class, recording : bool):
+ start_time = time.ticks_ms()
+
+ ssd.fill(0)
+
+ Writer.set_textpos(ssd, 0, 0) # In case previous tests have altered it
+ wri = Writer(ssd, fixed, verbose=False)
+ wri.set_clip(False, False, False)
+
+ text = f'Activity:'
+ text1 = Label(wri, 10, 10, wri.stringlen(text))
+ text1.value(text)
+
+ text2 = Label(wri, 30, 10, wri.stringlen(selected_class))
+ text2.value(selected_class)
+
+ state = '[recording]' if recording else '[ready]'
+ text3 = Label(wri, 80, 10, wri.stringlen(state))
+ text3.value(state)
+
+ refresh(ssd)
+
+ duration = time.ticks_ms() - start_time
+ print('render-display-done', duration)
+
+
+
+# Configuration
+classes = [
+ 'jumpingjack',
+ 'lunge',
+ 'squat',
+ 'other',
+]
+
+samplerate = 100
+chunk_length = 50
+file_duration = 10.0
+data_dir = 'har_record'
+
+
+def main():
+ mpu = MPU6886(I2C(0, sda=21, scl=22, freq=100000))
+
+ # Enable FIFO at a fixed samplerate
+ mpu.fifo_enable(True)
+ mpu.set_odr(samplerate)
+
+ chunk = bytearray(mpu.bytes_per_sample*chunk_length) # raw bytes
+ decoded = array.array('h', (0 for _ in range(3*chunk_length))) # decoded int16
+
+ # Internal LED on M5StickC PLUS2
+ led_pin = machine.Pin(19, machine.Pin.OUT)
+
+ # On M5StickC we need to set HOLD pin to stay alive when on battery
+ hold_pin = machine.Pin(4, machine.Pin.OUT)
+ hold_pin.value(1)
+
+ # Support cycling between classes, to indicate which is being recorded
+ class_selected = 0
+
+ def update_display():
+ c = classes[class_selected]
+ render_display(c, recorder._recording)
+ led_pin.value(1 if recorder._recording else 0)
+
+ def on_longpress():
+ # toggle recording state
+ if recorder._recording:
+ recorder.stop()
+ else:
+ recorder.start()
+ update_display()
+
+ def on_doubleclick():
+ # cycle between selected class
+ nonlocal class_selected
+ class_selected += 1
+ if class_selected >= len(classes):
+ class_selected = 0
+ c = classes[class_selected]
+ recorder.set_class(c)
+ update_display()
+
+ print(f'har-record-cycle class={c}')
+
+ button_pin = machine.Pin(37, machine.Pin.IN, machine.Pin.PULL_UP) # Button A on M5StickC PLUS2
+ button = Pushbutton(button_pin)
+ button.long_func(on_longpress, args=())
+ button.double_func(on_doubleclick, args=())
+
+ async def update_display_loop():
+ # workaround for display not always updating on boot
+ while True:
+ update_display()
+ await asyncio.sleep(1.0)
+
+ async def read_data():
+
+ while True:
+
+ # always read data from FIFO, to avoid overflow
+ count = mpu.get_fifo_count()
+ if count >= chunk_length:
+ start = time.ticks_ms()
+ mpu.read_samples_into(chunk)
+ decode_samples(chunk, decoded, mpu.bytes_per_sample)
+ #print(decoded)
+
+ # record data (if enabled)
+ recorder.process(decoded)
+
+ await asyncio.sleep(0.10)
+
+ async def run():
+ await asyncio.gather(update_display_loop(), read_data())
+
+ with Recorder(samplerate, file_duration, directory=data_dir) as recorder:
+
+ # UNCOMMENT to clean up data_dir
+ #recorder.delete()
+
+ print('har-record-ready')
+
+ asyncio.run(run())
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/har_trees/har_run.py b/examples/har_trees/har_run.py
index 11c3d8a..efdaa70 100644
--- a/examples/har_trees/har_run.py
+++ b/examples/har_trees/har_run.py
@@ -4,11 +4,22 @@
import zipfile
import npyfile
import emlearn_trees
+import timebased
+
+def argmax(arr):
+ idx_max = 0
+ value_max = arr[0]
+ for i in range(1, len(arr)):
+ if arr[i] > value_max:
+ value_max = arr[i]
+ idx_max = i
+
+ return idx_max
def har_load_test_data(path,
skip_samples=0, limit_samples=None):
- n_features = 92
+ n_features = timebased.N_FEATURES
with zipfile.ZipFile(path) as archive:
ext = '.npy'
@@ -53,19 +64,27 @@ def main():
model = emlearn_trees.new(10, 1000, 10)
+ dataset = 'har_uci'
+ dataset = 'har_exercise_1'
+
+ model_path = f'{dataset}_trees.csv'
+
# Load a CSV file with the model
- with open('har_uci_trees.csv', 'r') as f:
+ with open(model_path, 'r') as f:
emlearn_trees.load_model(model, f)
+ out = array.array('f', range(model.outputs()))
errors = 0
total = 0
- data_path = 'har_uci.testdata.npz'
+ data_path = f'{dataset}.testdata.npz'
+ print('har-run-load', data_path)
for features, labels in har_load_test_data(data_path):
assert len(labels) == 1
label = labels[0]
- result = model.predict(features)
+ model.predict(features, out)
+ result = argmax(out)
if result != label:
errors += 1
total += 1
diff --git a/examples/har_trees/har_train.py b/examples/har_trees/har_train.py
index ab5ca87..b67313d 100644
--- a/examples/har_trees/har_train.py
+++ b/examples/har_trees/har_train.py
@@ -12,8 +12,9 @@
import structlog
from sklearn.ensemble import RandomForestClassifier
-from sklearn.metrics import f1_score, make_scorer
+from sklearn.metrics import f1_score, make_scorer, get_scorer, PrecisionRecallDisplay
from sklearn.model_selection import GridSearchCV, GroupShuffleSplit
+from matplotlib import pyplot as plt
import emlearn
from emlearn.preprocessing.quantizer import Quantizer
@@ -22,7 +23,19 @@
log = structlog.get_logger()
-def evaluate(windows : pandas.DataFrame, groupby, hyperparameters,
+def train_test_split_grouped(X, y, groups, test_size=0.25, random_state=0):
+
+ gs = GroupShuffleSplit(n_splits=2, test_size=test_size, random_state=random_state)
+ train_ix, test_ix = next(gs.split(X, y, groups=groups))
+
+ X_train = X.iloc[train_ix]
+ X_test = X.iloc[test_ix]
+ Y_train = y.iloc[train_ix]
+ Y_test = y.iloc[test_ix]
+
+ return X_train, X_test, Y_train, Y_test
+
+def evaluate(windows : pandas.DataFrame, groupby : str, hyperparameters : dict,
random_state=1, n_splits=5, label_column='activity'):
# Setup subject-based cross validation
@@ -46,24 +59,49 @@ def evaluate(windows : pandas.DataFrame, groupby, hyperparameters,
verbose=2,
)
- feature_columns = sorted(set(windows.columns) - set([label_column]))
+ feature_columns = sorted(set(windows.columns) - set([label_column, groupby]))
assert 'subject' not in feature_columns
assert 'activity' not in feature_columns
+ assert 'file' not in feature_columns
X = windows[feature_columns]
Y = windows[label_column]
groups = windows.index.get_level_values(groupby)
- search.fit(X, Y, groups=groups)
- results = pandas.DataFrame(search.cv_results_)
+ # Split out test set
+ X_train, X_test, Y_train, Y_test = train_test_split_grouped(X, Y, groups=groups)
+ test_groups = list(X_test.index.get_level_values(groupby).unique())
+ groups_train = X_train.index.get_level_values(groupby)
+ train_groups = list(groups_train.unique())
+
+ # Run hyperparameter search using cross-validation
+ search.fit(X_train, Y_train, groups=groups_train)
+ cv_results = pandas.DataFrame(search.cv_results_)
estimator = search.best_estimator_
- return results, estimator
+ # Evaluate final estimator on train/test sets
+ scorer = get_scorer(f1_micro)
+ test_score = scorer(estimator, X_test, Y_test)
+ train_score = scorer(estimator, X_train, Y_train)
+
+ figures = dict()
+
+ if len(estimator.classes_) == 2:
+ # Binary classification, compute precision-recall curves
+ fig, ax = plt.subplots(1, figsize=(10, 10))
+ PrecisionRecallDisplay.from_estimator(estimator, X_train, Y_train, ax=ax, name='train')
+ PrecisionRecallDisplay.from_estimator(estimator, X_test, Y_test, ax=ax, name='test')
+ figures['precision_recall'] = fig
+
+ splits = (train_groups, test_groups)
+ scores = (train_score, test_score)
+ return cv_results, estimator, splits, scores, figures
def extract_windows(sensordata : pandas.DataFrame,
window_length : int,
window_hop : int,
groupby : list[str],
+ time_column = 'time',
):
groups = sensordata.groupby(groupby, observed=True)
@@ -74,7 +112,7 @@ def extract_windows(sensordata : pandas.DataFrame,
windows = []
# make sure order is correct
- group_df = group_df.reset_index().set_index('time').sort_index()
+ group_df = group_df.reset_index().set_index(time_column).sort_index()
# create windows
win_start = 0
@@ -166,6 +204,7 @@ def extract_features(sensordata : pandas.DataFrame,
quant_div = 4,
quant_depth = 6,
label_column='activity',
+ time_column='time',
) -> pandas.DataFrame:
"""
Convert sensor data into fixed-sized time windows and extact features
@@ -180,7 +219,7 @@ def extract_features(sensordata : pandas.DataFrame,
# Split into fixed-length windows
features_values = []
- generator = extract_windows(sensordata, window_length, window_hop, groupby=groupby)
+ generator = extract_windows(sensordata, window_length, window_hop, groupby=groupby, time_column=time_column)
for windows in generator:
# drop invalid data
@@ -222,6 +261,7 @@ def export_model(path, out):
def run_pipeline(run, hyperparameters, dataset,
data_dir,
out_dir,
+ model_settings=dict(),
n_splits=5,
features='timebased',
):
@@ -252,8 +292,31 @@ def run_pipeline(run, hyperparameters, dataset,
'running', 'rope_jumping',
],
),
+ 'har_exercise_1': dict(
+ groups=['file'],
+ data_columns = ['x', 'y', 'z'],
+ classes = [
+ #'mixed',
+ 'squat', 'jumpingjack', 'lunge', 'other',
+ ],
+ ),
+ 'toothbrush_hussain2021': dict(
+ groups=['subject'],
+ label_column = 'is_brushing',
+ time_column = 'elapsed',
+ data_columns = ['acc_x', 'acc_y', 'acc_z'],
+ #data_columns = ['gravity_x', 'gravity_y', 'gravity_z'],
+ #data_columns = ['motion_x', 'motion_y', 'motion_z'],
+ classes = [
+ #'mixed',
+ 'True', 'False',
+ ],
+ ),
}
+ if not dataset in dataset_config.keys():
+ raise ValueError(f"Unknown dataset {dataset}")
+
if not os.path.exists(out_dir):
os.makedirs(out_dir)
@@ -268,13 +331,25 @@ def run_pipeline(run, hyperparameters, dataset,
groups = dataset_config[dataset]['groups']
data_columns = dataset_config[dataset]['data_columns']
enabled_classes = dataset_config[dataset]['classes']
+ label_column = dataset_config[dataset].get('label_column', 'activity')
+ time_column = dataset_config[dataset].get('time_column', 'time')
+
+ data[label_column] = data[label_column].astype(str)
data_load_duration = time.time() - data_load_start
log.info('data-loaded', dataset=dataset, samples=len(data), duration=data_load_duration)
feature_extraction_start = time.time()
- features = extract_features(data, columns=data_columns, groupby=groups, features=features)
- labeled = numpy.count_nonzero(features['activity'].notna())
+ features = extract_features(data,
+ columns=data_columns,
+ groupby=groups,
+ features=features,
+ window_length=model_settings['window_length'],
+ window_hop=model_settings['window_hop'],
+ label_column=label_column,
+ time_column=time_column,
+ )
+ labeled = numpy.count_nonzero(features[label_column].notna())
feature_extraction_duration = time.time() - feature_extraction_start
log.info('feature-extraction-done',
@@ -285,20 +360,32 @@ def run_pipeline(run, hyperparameters, dataset,
)
# Drop windows without labels
- features = features[features.activity.notna()]
+ features = features[features[label_column].notna()]
# Keep only windows with enabled classes
- features = features[features.activity.isin(enabled_classes)]
+ features = features[features[label_column].isin(enabled_classes)]
- print('Class distribution\n', features['activity'].value_counts(dropna=False))
+ print('Class distribution\n', features[label_column].value_counts(dropna=False))
# Run train-evaluate
- results, estimator = evaluate(features,
+ evaluate_groupby = groups[0]
+ results, estimator, splits, scores, figures = evaluate(features,
hyperparameters=hyperparameters,
- groupby='subject',
+ groupby=evaluate_groupby,
n_splits=n_splits,
+ label_column=label_column,
)
+ print('Train-test splits')
+ for s in splits:
+ print(s)
+
+ # Save eval plots
+ for name, fig in figures.items():
+ p = os.path.join(out_dir, f'{dataset}.{name}.png')
+ fig.savefig(p)
+ print('Figure:', p)
+
# Save a model
estimator_path = os.path.join(out_dir, f'{dataset}.estimator.pickle')
with open(estimator_path, 'wb') as f:
@@ -309,7 +396,6 @@ def run_pipeline(run, hyperparameters, dataset,
export_model(estimator_path, model_path)
# Save testdata
- label_column = 'activity'
classes = estimator.classes_
class_mapping = dict(zip(classes, range(len(classes))))
meta_path = os.path.join(out_dir, f'{dataset}.meta.json')
@@ -362,6 +448,10 @@ def parse():
parser.add_argument('--features', type=str, default='timebased',
help='Which feature-set to use')
+ parser.add_argument('--window-length', type=int, default=128,
+ help='Length of each window to classify (in samples)')
+ parser.add_argument('--window-hop', type=int, default=64,
+ help='How far to hop for next window to classify (in samples)')
args = parser.parse_args()
@@ -378,12 +468,14 @@ def main():
run_id = uuid.uuid4().hex.upper()[0:6]
min_samples_leaf = config_number_list('MIN_SAMPLES_LEAF', '1,4,16,64,256')
+ max_leaf_nodes = config_number_list('MAX_LEAF_NODES', '4,8,16,32,64,128')
trees = config_number_list('TREES', '10')
hyperparameters = {
"max_features": [ 0.30 ],
'n_estimators': trees,
'min_samples_leaf': min_samples_leaf,
+ #'max_leaf_nodes': max_leaf_nodes,
}
results = run_pipeline(dataset=args.dataset,
@@ -391,6 +483,10 @@ def main():
data_dir=args.data_dir,
run=run_id,
hyperparameters=hyperparameters,
+ model_settings=dict(
+ window_hop=args.window_hop,
+ window_length=args.window_length,
+ ),
n_splits=int(os.environ.get('FOLDS', '5')),
features=args.features,
)
diff --git a/examples/har_trees/img/UCI-HAR-dataset.png b/examples/har_trees/img/UCI-HAR-dataset.png
new file mode 100644
index 0000000..1a39f66
Binary files /dev/null and b/examples/har_trees/img/UCI-HAR-dataset.png differ
diff --git a/examples/har_trees/img/exercise-combined.jpg b/examples/har_trees/img/exercise-combined.jpg
new file mode 100644
index 0000000..b1cc995
Binary files /dev/null and b/examples/har_trees/img/exercise-combined.jpg differ
diff --git a/examples/har_trees/img/exercise-combined.png b/examples/har_trees/img/exercise-combined.png
new file mode 100644
index 0000000..1ab6314
Binary files /dev/null and b/examples/har_trees/img/exercise-combined.png differ
diff --git a/examples/har_trees/img/har_exercises_classes.png b/examples/har_trees/img/har_exercises_classes.png
new file mode 100644
index 0000000..c4c09b8
Binary files /dev/null and b/examples/har_trees/img/har_exercises_classes.png differ
diff --git a/examples/har_trees/labeling/har_classify_config.xml b/examples/har_trees/labeling/har_classify_config.xml
new file mode 100644
index 0000000..dbb3ba1
--- /dev/null
+++ b/examples/har_trees/labeling/har_classify_config.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/har_trees/package.json b/examples/har_trees/package.json
index 1983039..6d26733 100644
--- a/examples/har_trees/package.json
+++ b/examples/har_trees/package.json
@@ -13,5 +13,5 @@
["fs:har_uci_trees.csv", "har_uci_trees.csv"],
["fs:har_uci.testdata.npz", "har_uci.testdata.npz"]
],
- "deps": [["emlearn", "master"], ["npyfile", "master"], ["zipfile", "master"]]
+ "deps": [["emlearn", "master"], ["https://github.com/jonnor/micropython-npyfile", "master"], ["https://github.com/jonnor/micropython-zipfile", "master"], ["https://github.com/jonnor/micropython-mpu6886", "master"]]
}
diff --git a/examples/har_trees/recorder.py b/examples/har_trees/recorder.py
new file mode 100644
index 0000000..4d96b59
--- /dev/null
+++ b/examples/har_trees/recorder.py
@@ -0,0 +1,98 @@
+
+import npyfile
+
+import asyncio
+import os
+import time
+import array
+import struct
+import gc
+
+
+def format_time(secs):
+ tt = time.gmtime(secs)
+ year, month, day, hour, minute, second, _, _ = tt[0:8]
+ formatted = f'{year:04d}-{month:02d}-{day:02d}T{hour:02d}{minute:02d}{second:02d}'
+ return formatted
+
+def file_or_dir_exists(filename):
+ try:
+ os.stat(filename)
+ return True
+ except OSError:
+ return False
+
+class Recorder():
+ """Record accelerometer data to .npy files"""
+
+ def __init__(self, samplerate, duration,
+ classname='unknown', directory='recorder_data', suffix='.npy'):
+ # config
+ self._directory = directory
+ assert directory[-1] != '/'
+ self._suffix = suffix
+ self._recording_samples = int(duration * samplerate)
+
+ # state
+ self._recording_file = None
+ self._recording = False
+ self._classname = classname
+
+ if not file_or_dir_exists(self._directory):
+ os.mkdir(self._directory)
+
+ def start(self):
+ self._recording = True
+ print('recorder-start')
+
+ def stop(self):
+ self.close()
+ self._recording = False
+ print('recorder-stop')
+
+ def set_class(self, name):
+ self._classname = name
+
+ def process(self, data):
+
+ if not self._recording:
+ return
+
+ t = time.ticks_ms()/1000.0
+
+ if self._recording_file is None:
+ # open file
+ time_str = format_time(time.time())
+ out_path = f'{self._directory}/{time_str}_{self._classname}{self._suffix}'
+ out_typecode = 'h'
+ out_shape = (self._recording_samples, 3)
+ self._recording_file_path = out_path
+ self._recording_file = npyfile.Writer(open(out_path, 'wb'), out_shape, out_typecode)
+ self._recording_file._write_header()
+ print(f'record-file-open t={t:.3f} file={out_path}')
+
+ # TODO: avoid writing too much at end of file
+ self._recording_file.write_values(data)
+ print(f'recorder-write-chunk t={t:.3f}')
+ if self._recording_file.written_bytes >= 3*2*self._recording_samples:
+ # rotate file
+ self.close()
+ print(f'record-file-rotate t={t:.3f}')
+
+ def delete(self):
+ for f in os.listdir(self._directory):
+ p = self._directory + '/' + f
+ print('recorder-delete-file', p)
+ os.unlink(p)
+
+ def close(self):
+ if self._recording_file is not None:
+ self._recording_file.close()
+ self._recording_file = None
+
+ # Support working as a context manager, to automatically clean up
+ def __enter__(self):
+ pass
+ return self
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ self.close()
diff --git a/examples/har_trees/requirements.txt b/examples/har_trees/requirements.txt
index 21a4f51..f337a66 100644
--- a/examples/har_trees/requirements.txt
+++ b/examples/har_trees/requirements.txt
@@ -5,3 +5,4 @@ emlearn
scikit-learn
setuptools
structlog
+matplotlib
diff --git a/examples/har_trees/test_recorder.py b/examples/har_trees/test_recorder.py
new file mode 100644
index 0000000..0396fff
--- /dev/null
+++ b/examples/har_trees/test_recorder.py
@@ -0,0 +1,39 @@
+
+import array
+import math
+
+import npyfile
+
+print(npyfile.__file__)
+
+from recorder import Recorder
+
+samplerate = 100
+chunk_length = 30
+file_duration = 2.0
+test_data_dir = 'test_har_record'
+
+def check_file(path):
+ shape, data = npyfile.load(path)
+ return shape
+
+def test_recorder_one_file():
+
+ decoded = array.array('h', (0 for _ in range(3*chunk_length)))
+
+ n_chunks = math.ceil((file_duration * samplerate) / chunk_length)
+
+ with Recorder(samplerate, file_duration, directory=test_data_dir) as recorder:
+ recorder.start()
+
+ for i in range(n_chunks):
+ recorder.process(decoded)
+
+ p = recorder._recording_file_path
+
+ shape = check_file(p)
+ assert shape[0] == 3
+ assert shape[1] == int(file_duration*samplerate)
+
+if __name__ == '__main__':
+ test_recorder_one_file()
diff --git a/examples/mnist_cnn/README.md b/examples/mnist_cnn/README.md
index e1f15c4..5e94450 100644
--- a/examples/mnist_cnn/README.md
+++ b/examples/mnist_cnn/README.md
@@ -8,7 +8,7 @@ Make sure to have Python **3.10** installed.
At the time TinyMaix, used for CNN, only supports Tensorflow <2.14,
which is not supported on Python 3.12 or later.
-Make sure to have the Unix port of MicroPython 1.23 setup.
+Make sure to have the Unix port of MicroPython setup.
On Windows you can use Windows Subsystem for Linux (WSL), or Docker.
Install the dependencies of this example:
@@ -55,7 +55,7 @@ mpremote mip install https://emlearn.github.io/emlearn-micropython/builds/master
```console
mpremote cp mnist_cnn.tmdl :
-mpremote cp -r data/ :
+mpremote cp -r test_data/ :
mpremote run mnist_cnn_run.py
```
diff --git a/examples/mnist_cnn/convert.py b/examples/mnist_cnn/convert.py
new file mode 100644
index 0000000..057a2d6
--- /dev/null
+++ b/examples/mnist_cnn/convert.py
@@ -0,0 +1,11 @@
+
+import sys
+import numpy
+import skimage.io
+
+p = sys.argv[1]
+o = sys.argv[2]
+a = numpy.load(p)
+print(a.shape)
+
+skimage.io.imsave(o, a)
diff --git a/examples/mnist_cnn/downscale.py b/examples/mnist_cnn/downscale.py
new file mode 100644
index 0000000..a400b87
--- /dev/null
+++ b/examples/mnist_cnn/downscale.py
@@ -0,0 +1,53 @@
+
+import array
+import time
+
+@micropython.native
+def average2d(inp, rowstride : int, x : int, y : int, size : int):
+ acc : int = 0
+ for r in range(y, y+size):
+ for c in range(x, x+size):
+ acc += inp[(r*rowstride)+c]
+
+ avg = acc // (size*size)
+ return avg
+
+@micropython.native
+def downscale(inp, out, in_size : int, out_size : int):
+
+ # assumes square, single-channel (grayscale) images
+ assert len(inp) == in_size*in_size
+ assert len(out) == out_size*out_size
+ assert (in_size % out_size) == 0, (in_size, out_size)
+ factor : int = in_size // out_size
+
+ for row in range(out_size):
+ for col in range(out_size):
+ o : int = (row * out_size) + col
+ out[o] = average2d(inp, in_size, col*factor, row*factor, factor)
+
+
+def test_downscale():
+
+ import npyfile
+
+ shape, data = npyfile.load('inp.npy')
+ #print(data)
+
+ npyfile.save('orig.npy', data, shape)
+
+ insize = 96
+ outsize = 32
+ out = array.array('B', (0 for _ in range(outsize*outsize)))
+
+ start = time.ticks_ms()
+ downscale(data, out, insize, outsize)
+ duration = time.ticks_diff(time.ticks_ms(), start)
+ print('downscale', duration)
+
+ npyfile.save('out.npy', out, (outsize, outsize))
+
+
+
+if __name__ == '__main__':
+ test_downscale()
diff --git a/examples/mnist_cnn/downscalecheck.py b/examples/mnist_cnn/downscalecheck.py
new file mode 100644
index 0000000..8fa8b86
--- /dev/null
+++ b/examples/mnist_cnn/downscalecheck.py
@@ -0,0 +1,17 @@
+
+import skimage.io
+import skimage.color
+import skimage.transform
+import numpy
+
+p = 'data/rps-cv-images/rock/dnss2tOuxRmL0ZjZ.png'
+a = skimage.io.imread(p)
+print(a.shape, a.dtype)
+
+o = skimage.color.rgb2gray(a)
+o = skimage.transform.resize(o, (96, 96))
+o = (o * 255).astype(numpy.uint8)
+
+print(o.shape, o.dtype)
+
+numpy.save('inp.npy', o, allow_pickle=False)
diff --git a/examples/mnist_cnn/mnist_cnn.h b/examples/mnist_cnn/mnist_cnn.h
index 62db61c..08205c0 100644
--- a/examples/mnist_cnn/mnist_cnn.h
+++ b/examples/mnist_cnn/mnist_cnn.h
@@ -5,250 +5,250 @@
#define MDL_BUF_LEN (2136)
#define LBUF_LEN (2232)
const uint8_t mdl_data[3912]={\
- 0x4d, 0x41, 0x49, 0x58, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x06, 0x00, 0x58, 0x08, 0x00, 0x00,
+ 0x4d, 0x41, 0x49, 0x58, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x06, 0x00, 0x58, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00,
0x03, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x08, 0x00,
- 0x81, 0x80, 0x80, 0x3b, 0x80, 0xff, 0xff, 0xff, 0xc9, 0x82, 0x86, 0x3c, 0x80, 0xff, 0xff, 0xff,
+ 0x81, 0x80, 0x80, 0x3b, 0x80, 0xff, 0xff, 0xff, 0xed, 0xea, 0x86, 0x3c, 0x80, 0xff, 0xff, 0xff,
0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00,
- 0x6c, 0x76, 0x30, 0x3c, 0xb8, 0xf0, 0x90, 0x3c, 0x9d, 0x17, 0x7a, 0x3c, 0x2d, 0xae, 0x53, 0x3c,
- 0x3f, 0x35, 0xf1, 0x3b, 0x5b, 0x0f, 0xf9, 0x3b, 0x70, 0x38, 0x0c, 0x3c, 0x70, 0x3d, 0x93, 0x3c,
- 0x81, 0x8f, 0xf8, 0xf4, 0xf1, 0x3c, 0x34, 0x67, 0x7b, 0xf1, 0x1a, 0xea, 0x3e, 0x37, 0x9a, 0x4b,
- 0x1b, 0x81, 0xe7, 0xe3, 0x81, 0x21, 0xaf, 0x12, 0xc3, 0xc1, 0xdf, 0x4f, 0xd5, 0x96, 0x7f, 0xf7,
- 0xad, 0x62, 0x1c, 0xdb, 0x26, 0x20, 0xe8, 0x7f, 0x41, 0x25, 0x4a, 0x18, 0x30, 0x12, 0x4b, 0xed,
- 0x1d, 0x7f, 0x5e, 0xd2, 0x28, 0xcd, 0x95, 0x60, 0x7f, 0xb2, 0xfb, 0x2f, 0x15, 0x58, 0x2c, 0xa7,
- 0xa1, 0xcf, 0x43, 0x11, 0x81, 0x36, 0x4e, 0xd7, 0x4c, 0x1e, 0x00, 0x00, 0x51, 0xf2, 0xff, 0xff,
- 0x0a, 0x97, 0xff, 0xff, 0x5e, 0x14, 0x00, 0x00, 0x99, 0x9e, 0x00, 0x00, 0x6e, 0x5b, 0x00, 0x00,
- 0x0c, 0x5b, 0x00, 0x00, 0xde, 0xca, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00,
+ 0x4f, 0x03, 0x43, 0x3c, 0xff, 0xaf, 0x2d, 0x3c, 0xbd, 0x82, 0x41, 0x3c, 0x07, 0x2c, 0x4a, 0x3c,
+ 0x1a, 0x13, 0x7e, 0x3c, 0x19, 0xe0, 0xb0, 0x3c, 0x80, 0x3f, 0x7d, 0x3c, 0xf9, 0xa8, 0xbc, 0x3b,
+ 0x96, 0x3c, 0x36, 0x81, 0x30, 0x43, 0xcb, 0x4d, 0x33, 0x62, 0xb1, 0x81, 0x68, 0x5c, 0x8b, 0x33,
+ 0x66, 0xf7, 0x1b, 0xb5, 0x96, 0x14, 0x89, 0x57, 0xa4, 0x81, 0x34, 0xb4, 0x3f, 0x59, 0xfb, 0xdf,
+ 0x56, 0xbc, 0x81, 0x20, 0x81, 0xab, 0xba, 0x06, 0x0f, 0xfb, 0x18, 0x3f, 0x3a, 0x81, 0x17, 0x07,
+ 0xed, 0xb3, 0xfb, 0x08, 0x0b, 0xc1, 0xb2, 0x81, 0xcf, 0x4d, 0x30, 0xbd, 0x56, 0x43, 0x00, 0xe7,
+ 0x32, 0x65, 0x4b, 0xfb, 0x7f, 0x5a, 0x2d, 0x7c, 0x1c, 0x1e, 0x00, 0x00, 0xe3, 0x28, 0x00, 0x00,
+ 0x35, 0xa2, 0xff, 0xff, 0x76, 0xf1, 0xff, 0xff, 0x21, 0xda, 0xff, 0xff, 0x42, 0xbb, 0xff, 0xff,
+ 0x41, 0xe7, 0xff, 0xff, 0x86, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00,
0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x08, 0x00,
- 0x03, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0c, 0x00, 0xc9, 0x82, 0x86, 0x3c, 0x80, 0xff, 0xff, 0xff,
- 0x4a, 0x88, 0x7a, 0x3c, 0x80, 0xff, 0xff, 0xff, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00,
+ 0x03, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0c, 0x00, 0xed, 0xea, 0x86, 0x3c, 0x80, 0xff, 0xff, 0xff,
+ 0x3d, 0x08, 0x7e, 0x3c, 0x80, 0xff, 0xff, 0xff, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
- 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x78, 0xbe, 0x13, 0x3b, 0xce, 0x44, 0x60, 0x3b,
- 0x3c, 0x63, 0xc2, 0x3b, 0xcf, 0xa1, 0x22, 0x3b, 0x9c, 0x79, 0x84, 0x3b, 0x08, 0x15, 0x39, 0x3b,
- 0x6a, 0x40, 0x5f, 0x3b, 0x07, 0xd0, 0x87, 0x3b, 0x13, 0xc9, 0x3b, 0x3b, 0x29, 0x83, 0x2f, 0x3b,
- 0xaf, 0x56, 0x6c, 0x3b, 0xe6, 0x45, 0x3d, 0x3b, 0xbb, 0x03, 0x3d, 0x68, 0x56, 0x56, 0x32, 0xdb,
- 0xd3, 0xdf, 0xee, 0x1c, 0xe7, 0x28, 0x3d, 0xf4, 0xc8, 0x14, 0xaa, 0xdf, 0x33, 0x0d, 0x9f, 0xd8,
- 0xc0, 0x00, 0x45, 0x26, 0x12, 0xde, 0xeb, 0xba, 0xe8, 0xce, 0x98, 0xbf, 0x15, 0xe9, 0x1b, 0x5c,
- 0x24, 0x23, 0x50, 0xc0, 0x81, 0xed, 0x25, 0xff, 0xf3, 0x12, 0x17, 0x23, 0x1f, 0xd4, 0xfc, 0x2e,
- 0xfd, 0x13, 0xf5, 0x45, 0x02, 0xdc, 0x96, 0x36, 0xfc, 0xe4, 0xf2, 0xc1, 0xce, 0x93, 0xd8, 0xfa,
- 0x29, 0x30, 0x17, 0xc8, 0x07, 0x02, 0x6e, 0x20, 0x3b, 0x0a, 0x0d, 0xfd, 0xd8, 0xf1, 0x14, 0x2c,
- 0xfb, 0x2a, 0xbb, 0x81, 0x96, 0xfa, 0x1e, 0xe3, 0x03, 0x36, 0x17, 0x1d, 0x0c, 0xec, 0x11, 0xd9,
- 0xc7, 0xc7, 0xe2, 0xd2, 0xfa, 0xf9, 0xe9, 0xfb, 0xe6, 0xe3, 0xc6, 0xd8, 0xf0, 0x3c, 0xf7, 0x11,
- 0x04, 0xe7, 0xdb, 0xdf, 0xb6, 0xd6, 0x12, 0x19, 0x02, 0xf5, 0xd8, 0xf7, 0xd2, 0x02, 0x08, 0xbf,
- 0xbb, 0xdf, 0x27, 0xe7, 0xdd, 0xd0, 0xa3, 0xdd, 0x19, 0x2e, 0x1a, 0x21, 0x2f, 0x16, 0xf8, 0xe9,
- 0x09, 0x05, 0x16, 0x0b, 0x19, 0xf0, 0x12, 0x11, 0x0c, 0x06, 0xe6, 0x1e, 0x7f, 0x13, 0x4d, 0x27,
- 0x30, 0x1a, 0xef, 0xfc, 0xd1, 0xce, 0xec, 0xca, 0xf4, 0xf7, 0x09, 0x19, 0x00, 0x10, 0xf9, 0xd4,
- 0xf7, 0x1b, 0xce, 0x16, 0x19, 0xff, 0xec, 0xdd, 0x03, 0xde, 0x0b, 0xee, 0xfe, 0x14, 0x12, 0xff,
- 0xf6, 0xf9, 0x0b, 0x08, 0xe7, 0x05, 0x1c, 0xe0, 0xd9, 0xf1, 0xcc, 0xeb, 0xe2, 0x1e, 0x17, 0xe6,
- 0x0d, 0xdd, 0xe5, 0x39, 0xee, 0x03, 0x02, 0x97, 0xb2, 0x1b, 0x24, 0x12, 0x1f, 0x10, 0x0c, 0x00,
- 0x0b, 0x04, 0xf2, 0x46, 0x67, 0x37, 0x7f, 0x5f, 0x2b, 0x6a, 0x22, 0x43, 0x52, 0x27, 0x30, 0x13,
- 0xf7, 0x4a, 0x03, 0xdb, 0x04, 0xdf, 0x37, 0x01, 0x18, 0x00, 0xfd, 0x0e, 0x9d, 0xf2, 0x19, 0xcb,
- 0x0e, 0xfc, 0x93, 0xdb, 0xe4, 0xd5, 0x47, 0x0b, 0x0f, 0x34, 0x2f, 0x92, 0x1e, 0xfa, 0xd1, 0xcb,
- 0xcc, 0xdc, 0x93, 0x95, 0xa1, 0xa4, 0xdf, 0xfa, 0x1a, 0xe8, 0xc0, 0x11, 0x0e, 0x1b, 0xec, 0xf4,
- 0x09, 0xa6, 0x81, 0xb5, 0x22, 0xf1, 0xf3, 0x15, 0x05, 0xed, 0xe2, 0xc3, 0xec, 0xf0, 0xeb, 0xd7,
- 0xc7, 0xb7, 0xcd, 0xb0, 0xbf, 0xdb, 0x0b, 0xf4, 0xf3, 0x14, 0x1b, 0x24, 0xdf, 0xea, 0xac, 0xf9,
- 0x00, 0x4d, 0xe2, 0x05, 0x17, 0xf9, 0xbc, 0xda, 0xfd, 0x15, 0x18, 0xde, 0x1a, 0x1f, 0xf6, 0x0a,
- 0x2c, 0xef, 0xb8, 0xbc, 0xff, 0xe7, 0xed, 0x50, 0x54, 0x60, 0xfb, 0x4a, 0x3b, 0xdf, 0xf8, 0x0f,
- 0xc7, 0xc5, 0xf7, 0xcc, 0xd7, 0x1d, 0xc5, 0xa7, 0xfb, 0xf6, 0x08, 0xf6, 0x26, 0x37, 0x10, 0x45,
- 0x0e, 0xf3, 0x81, 0xe4, 0xd9, 0x3c, 0x09, 0xb4, 0x11, 0xdc, 0xa4, 0x58, 0x20, 0xfb, 0x2d, 0x10,
- 0x05, 0x2d, 0x41, 0x28, 0x2c, 0x13, 0x22, 0xb7, 0x22, 0x21, 0xe7, 0x09, 0x02, 0x14, 0xe8, 0x06,
- 0xf6, 0xf1, 0x02, 0xd2, 0xcb, 0x18, 0x1c, 0x1c, 0x21, 0x0e, 0x32, 0x3c, 0x2c, 0x27, 0x14, 0xda,
- 0xda, 0xe4, 0x17, 0x24, 0xdf, 0xca, 0xf5, 0xe6, 0x27, 0x01, 0x0d, 0x0e, 0xfc, 0x30, 0xfc, 0xf5,
- 0xe7, 0x01, 0xf6, 0x01, 0xd4, 0xc7, 0x00, 0xcc, 0xa7, 0xf9, 0x8a, 0xab, 0xc1, 0xb6, 0xb9, 0xa5,
- 0xc6, 0xa8, 0x2a, 0x2e, 0xff, 0xdd, 0x43, 0xf6, 0xc0, 0x3e, 0x01, 0xdc, 0x13, 0x1c, 0xe3, 0x0f,
- 0x10, 0x11, 0xe8, 0x1d, 0xbf, 0x02, 0x16, 0xe2, 0x14, 0x2b, 0xf0, 0x1c, 0x02, 0x10, 0x28, 0xe4,
- 0xe6, 0x0b, 0xc6, 0xbb, 0xdd, 0xab, 0x81, 0x82, 0xbc, 0xff, 0xec, 0x29, 0x26, 0x49, 0x50, 0x32,
- 0xda, 0xb5, 0x0b, 0xf0, 0xc5, 0x09, 0x23, 0xee, 0x02, 0xc6, 0xe3, 0xed, 0x01, 0x19, 0x18, 0x60,
- 0x44, 0x1f, 0xfb, 0x2f, 0x54, 0xc2, 0x43, 0x60, 0xc1, 0xf8, 0x32, 0xf6, 0x1a, 0x3b, 0x0f, 0x4a,
- 0x7f, 0x0b, 0x3b, 0x4d, 0xe7, 0xc7, 0x0c, 0x01, 0xb0, 0xcf, 0x0a, 0xb2, 0xd6, 0x09, 0x25, 0xeb,
- 0xf7, 0x1e, 0xe1, 0xf2, 0x07, 0xf1, 0xd2, 0xaa, 0xf3, 0x06, 0x0a, 0x2e, 0x20, 0x31, 0x16, 0x63,
- 0x5d, 0x3f, 0x34, 0x16, 0xd6, 0xc3, 0xdb, 0xd8, 0xce, 0x15, 0xed, 0xce, 0x20, 0x24, 0xf9, 0x3e,
- 0x33, 0x04, 0x2b, 0xfc, 0xc8, 0xd0, 0x3b, 0xe5, 0x47, 0x47, 0xd2, 0x4d, 0x51, 0x1e, 0x2d, 0xf6,
- 0x32, 0xd8, 0xdb, 0x2b, 0x20, 0xf0, 0x34, 0xef, 0x02, 0x1a, 0xee, 0x22, 0xe6, 0xcd, 0x06, 0x0b,
- 0xf3, 0xfa, 0xfc, 0xf5, 0xeb, 0x05, 0xf8, 0xfd, 0xf2, 0xea, 0x05, 0xeb, 0x2b, 0xe4, 0x1d, 0xce,
- 0x01, 0x01, 0x2e, 0x2c, 0xf2, 0x2a, 0x53, 0xfc, 0xea, 0xf0, 0x08, 0x01, 0x92, 0x2d, 0xdf, 0x81,
- 0x14, 0x0d, 0x12, 0xd5, 0x21, 0x19, 0xbd, 0x01, 0xec, 0xcf, 0xe6, 0x05, 0x86, 0xe8, 0x04, 0x81,
- 0xd1, 0xfa, 0x0f, 0x03, 0xe2, 0x09, 0xe0, 0xdd, 0x19, 0x1c, 0x0c, 0xf7, 0x05, 0x19, 0x21, 0x05,
- 0x36, 0xc5, 0xd0, 0x00, 0xe4, 0x08, 0xeb, 0xf5, 0x3b, 0x04, 0xc2, 0xf5, 0xd2, 0xe5, 0x35, 0xfa,
- 0xf2, 0x40, 0x2a, 0xf8, 0x3b, 0x11, 0xe6, 0x16, 0x23, 0x20, 0x21, 0x0e, 0x16, 0xfe, 0xf5, 0x03,
- 0xef, 0xef, 0xf0, 0xf5, 0xdf, 0x4e, 0xee, 0x1a, 0xcf, 0x0c, 0x29, 0xbd, 0x0f, 0xfe, 0xee, 0x03,
- 0xf5, 0x9e, 0xbd, 0xf3, 0x97, 0xbf, 0xfe, 0x04, 0x1b, 0x2e, 0xdb, 0xb0, 0xd0, 0xda, 0x04, 0x4f,
- 0x19, 0x32, 0x7f, 0x06, 0xf0, 0xa8, 0xdb, 0xdf, 0xb3, 0xe0, 0x00, 0xfd, 0xf7, 0x3e, 0x06, 0xca,
- 0xd7, 0xde, 0x16, 0xdc, 0x0e, 0x27, 0x21, 0x0b, 0x2c, 0x21, 0xcd, 0xce, 0xad, 0xfb, 0xfc, 0x01,
- 0xee, 0x0c, 0xd9, 0xe0, 0xfd, 0x0a, 0x0e, 0x1d, 0x07, 0xf4, 0x60, 0x09, 0x3c, 0x02, 0x18, 0x26,
- 0x02, 0xe6, 0xe8, 0x17, 0xef, 0xee, 0x52, 0x2d, 0x2e, 0xd1, 0xea, 0x06, 0x8a, 0xa3, 0x98, 0x13,
- 0xf3, 0xdd, 0xe8, 0x28, 0x3f, 0x08, 0x17, 0x4d, 0x03, 0x81, 0xbe, 0x1f, 0x17, 0x1e, 0xe3, 0xb1,
- 0xe0, 0xfa, 0xbf, 0x0e, 0x18, 0xe9, 0x3e, 0xd9, 0xe6, 0xc5, 0x4c, 0x0f, 0x18, 0x15, 0x01, 0x10,
- 0xc8, 0x94, 0x08, 0x3d, 0x39, 0x32, 0x10, 0x1c, 0x15, 0x13, 0x03, 0xe2, 0xdd, 0xf0, 0xdd, 0x10,
- 0xeb, 0xc0, 0x1c, 0x6d, 0x4b, 0xe4, 0x34, 0x37, 0x1c, 0x81, 0xff, 0xff, 0xeb, 0xd3, 0xfe, 0xff,
- 0xe0, 0x42, 0x00, 0x00, 0x0d, 0x07, 0x00, 0x00, 0x04, 0xd9, 0xfe, 0xff, 0xe2, 0x13, 0x00, 0x00,
- 0x2e, 0xa4, 0xfe, 0xff, 0x87, 0xa0, 0x00, 0x00, 0xf7, 0x71, 0x00, 0x00, 0x5d, 0x9a, 0xff, 0xff,
- 0x13, 0x30, 0xff, 0xff, 0x6e, 0xc8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x78, 0x08, 0x00, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x3c, 0x8e, 0x77, 0x3b, 0x94, 0x22, 0x2c, 0x3b,
+ 0x11, 0x30, 0xa8, 0x3b, 0xc1, 0x4a, 0x37, 0x3b, 0x53, 0x1b, 0x20, 0x3b, 0xd0, 0x77, 0x37, 0x3b,
+ 0x44, 0xfa, 0x20, 0x3b, 0xef, 0x62, 0xf8, 0x3a, 0xa6, 0xf0, 0x5a, 0x3b, 0xc5, 0xf1, 0x82, 0x3b,
+ 0xc1, 0x4a, 0x4c, 0x3b, 0xcf, 0x27, 0xa3, 0x3b, 0x38, 0x31, 0x0c, 0x1a, 0xb5, 0xd0, 0x15, 0xbf,
+ 0xea, 0x54, 0x0d, 0xcf, 0x00, 0xa3, 0x9e, 0xc4, 0xac, 0xec, 0x27, 0x0c, 0x0b, 0xd9, 0x18, 0x12,
+ 0x29, 0xeb, 0x2a, 0xff, 0x04, 0xe9, 0x06, 0x11, 0x05, 0x29, 0xdf, 0xdc, 0x02, 0x11, 0xf1, 0xd2,
+ 0x7f, 0x60, 0x3e, 0x3c, 0x3b, 0x35, 0xb4, 0xb8, 0x69, 0x23, 0xe2, 0x2f, 0xb8, 0xc0, 0xc4, 0xd0,
+ 0x0f, 0xeb, 0xb1, 0x35, 0x0d, 0x0a, 0xf1, 0x11, 0x35, 0x2c, 0x03, 0x00, 0xdd, 0x95, 0xc3, 0x37,
+ 0x2e, 0x34, 0xf5, 0x27, 0x2b, 0xd2, 0xc8, 0x95, 0x90, 0x4c, 0x24, 0x22, 0x40, 0x29, 0xfe, 0xd8,
+ 0xed, 0xe1, 0x27, 0xea, 0x07, 0xf7, 0xc0, 0x94, 0x1e, 0xcf, 0xc3, 0x1e, 0x26, 0x3a, 0xe4, 0xe8,
+ 0x0a, 0x17, 0xeb, 0x06, 0xc5, 0xaf, 0x02, 0xb2, 0xf6, 0xf7, 0x96, 0x38, 0xd9, 0xda, 0x21, 0x0c,
+ 0xcd, 0xe7, 0xba, 0x17, 0x7f, 0x33, 0xfc, 0x03, 0xef, 0xf6, 0xdd, 0x0c, 0x88, 0xe3, 0x1a, 0x08,
+ 0xf8, 0xd2, 0x5a, 0x70, 0x1d, 0xfd, 0x02, 0xf0, 0xc7, 0xa8, 0xfd, 0xe5, 0xb3, 0xd0, 0x0a, 0xf7,
+ 0xf2, 0x97, 0xd1, 0xe2, 0xea, 0x01, 0x11, 0x17, 0xf9, 0xfb, 0xd0, 0x81, 0xee, 0xfe, 0x9b, 0xae,
+ 0xef, 0xd8, 0xcf, 0x17, 0x45, 0x14, 0x0e, 0x2d, 0x10, 0x0f, 0x1d, 0x15, 0x5b, 0x37, 0x14, 0x22,
+ 0x23, 0x05, 0xf0, 0xe4, 0xef, 0x75, 0x52, 0x23, 0x17, 0x2d, 0x4d, 0xe5, 0x0a, 0x19, 0x19, 0x33,
+ 0x15, 0x07, 0x25, 0x09, 0x0f, 0x41, 0x28, 0xe1, 0xf4, 0xce, 0xd2, 0x1d, 0x1f, 0xdb, 0xf8, 0x25,
+ 0x40, 0x14, 0x2a, 0x4e, 0x2a, 0x1a, 0x15, 0x17, 0xe8, 0xad, 0xeb, 0xdb, 0xf3, 0x07, 0x11, 0xfd,
+ 0xd7, 0xf9, 0x1d, 0x03, 0x06, 0x1a, 0x47, 0x59, 0xe3, 0x27, 0x16, 0x20, 0x3e, 0x09, 0x10, 0x01,
+ 0xb6, 0xf5, 0xc8, 0xf9, 0x73, 0x1d, 0xfd, 0x19, 0xe2, 0xf8, 0x3c, 0xee, 0x0f, 0x7d, 0x1a, 0xe6,
+ 0x52, 0xd3, 0xda, 0x14, 0x0c, 0x45, 0xaf, 0xef, 0xd7, 0xce, 0x01, 0xa3, 0xc8, 0xd5, 0x96, 0xa2,
+ 0xdf, 0xcc, 0x02, 0x08, 0x9d, 0x40, 0x51, 0x81, 0xfb, 0xe1, 0xb3, 0xf1, 0xc9, 0xad, 0x0c, 0xdb,
+ 0xe3, 0xb6, 0x0c, 0xb0, 0xce, 0xda, 0xac, 0xe0, 0xb1, 0xdd, 0x2e, 0x1f, 0x60, 0x0d, 0x23, 0x3c,
+ 0x07, 0x1e, 0x41, 0x10, 0xdb, 0xd5, 0x0b, 0xff, 0xa5, 0xf6, 0xac, 0x9d, 0x54, 0x10, 0x2d, 0x30,
+ 0x0d, 0x2a, 0x37, 0x11, 0xf2, 0x7b, 0x2d, 0x30, 0x5f, 0x3d, 0xd7, 0x59, 0xfd, 0x25, 0x9c, 0xf7,
+ 0xac, 0x8f, 0xdc, 0xbf, 0xd5, 0xf3, 0xd7, 0xe6, 0xd3, 0xe0, 0xf3, 0x7f, 0x4b, 0xd7, 0x45, 0xec,
+ 0xf3, 0xe5, 0xe6, 0xfb, 0xf4, 0x03, 0x12, 0xea, 0x20, 0x57, 0x1e, 0xd9, 0x0f, 0x02, 0xda, 0x0d,
+ 0xe9, 0xf2, 0xaf, 0xb6, 0x98, 0x28, 0x69, 0x58, 0x0d, 0x44, 0x7f, 0xf7, 0x10, 0x00, 0x18, 0xd4,
+ 0xf5, 0x37, 0x2e, 0xfd, 0xff, 0x0e, 0x24, 0x0b, 0xe1, 0xcb, 0x16, 0xde, 0x91, 0xbb, 0xb0, 0x8a,
+ 0x14, 0x2b, 0xe4, 0x07, 0xfd, 0x04, 0x04, 0xe8, 0x1f, 0xf6, 0xc0, 0xb5, 0xf0, 0xe1, 0xe8, 0x45,
+ 0x3a, 0x0b, 0x1f, 0xf8, 0xe3, 0xda, 0xfa, 0xc0, 0x29, 0x47, 0x21, 0x0c, 0x4f, 0x3d, 0x11, 0x5d,
+ 0x52, 0x55, 0x35, 0x43, 0x0f, 0x0d, 0xfe, 0x15, 0x5b, 0x23, 0xf5, 0x24, 0x43, 0x05, 0x45, 0x19,
+ 0xe4, 0x22, 0x01, 0x12, 0x35, 0x30, 0x28, 0x47, 0x68, 0x29, 0x10, 0x1b, 0x9e, 0xb2, 0xc1, 0xc4,
+ 0x8a, 0x82, 0xd8, 0x88, 0x90, 0xeb, 0x01, 0xf2, 0x39, 0x2e, 0xf1, 0x38, 0x10, 0x81, 0xdd, 0x14,
+ 0x04, 0xfb, 0xdf, 0xf6, 0x0b, 0xf8, 0xf5, 0x11, 0xa4, 0x12, 0xac, 0xe9, 0xdf, 0xaa, 0x44, 0x41,
+ 0xf9, 0x27, 0x00, 0xdd, 0xc5, 0xfd, 0x1f, 0xdc, 0x38, 0x72, 0x40, 0xf9, 0x42, 0x05, 0xd5, 0x25,
+ 0xef, 0x08, 0xca, 0xa0, 0xb5, 0xe3, 0x94, 0xca, 0xad, 0x81, 0xed, 0xfd, 0x0d, 0x5b, 0x36, 0x3d,
+ 0x45, 0x2c, 0x01, 0x15, 0xa7, 0x8e, 0xc6, 0xc7, 0xa2, 0xfa, 0xac, 0xf3, 0xde, 0xcf, 0xba, 0xc4,
+ 0x4b, 0x2d, 0x0a, 0xfc, 0xec, 0xa0, 0xf8, 0x1f, 0x60, 0x2c, 0x60, 0x6b, 0x3e, 0x2b, 0xf4, 0x34,
+ 0x1f, 0x65, 0xe2, 0xd7, 0x5f, 0x0e, 0xd6, 0x05, 0x0a, 0xff, 0xbd, 0x13, 0xf4, 0xd8, 0x31, 0x37,
+ 0x27, 0xb2, 0x1c, 0x45, 0xf6, 0x14, 0x03, 0xec, 0x3b, 0x22, 0x60, 0x48, 0xf3, 0x24, 0xe8, 0xc6,
+ 0x26, 0x10, 0x0c, 0xe3, 0xb5, 0x08, 0x1a, 0xf5, 0xeb, 0x20, 0x1a, 0x01, 0x46, 0xf9, 0x21, 0xf7,
+ 0xa0, 0xe4, 0xb9, 0xd8, 0xe5, 0x71, 0x12, 0x5f, 0x7f, 0x3c, 0x46, 0x2d, 0x13, 0xf8, 0xb6, 0xff,
+ 0x27, 0xbb, 0x07, 0x26, 0xd7, 0x05, 0xfa, 0xaa, 0xce, 0xc5, 0xd6, 0xee, 0x0d, 0x16, 0xf9, 0x20,
+ 0x09, 0x47, 0x01, 0x10, 0x21, 0xda, 0x0c, 0x2b, 0x12, 0x1d, 0x3a, 0x22, 0x16, 0x2a, 0x1a, 0x0a,
+ 0x14, 0x21, 0xf9, 0x64, 0x32, 0x25, 0x7f, 0x3b, 0x3a, 0x49, 0x1b, 0xfc, 0xb3, 0xc1, 0x07, 0x9c,
+ 0xff, 0xfb, 0xe9, 0xf4, 0xdb, 0xfa, 0x4a, 0xb1, 0xba, 0xd9, 0xde, 0xc6, 0xc2, 0xe3, 0x4d, 0x04,
+ 0xc8, 0x29, 0xf9, 0xa5, 0xe4, 0x23, 0x0b, 0xc4, 0xc1, 0x0a, 0xaf, 0x99, 0xee, 0xbc, 0xd7, 0x48,
+ 0x0a, 0xbf, 0x0d, 0xf3, 0xdd, 0x1e, 0xf2, 0xba, 0xcf, 0xf6, 0x55, 0xff, 0x13, 0x2b, 0x1b, 0x23,
+ 0xf8, 0x35, 0x64, 0x70, 0xf2, 0x19, 0x2a, 0x13, 0x05, 0x0c, 0x81, 0x03, 0x6e, 0xe8, 0x2d, 0x5f,
+ 0x01, 0x15, 0xf2, 0x34, 0x5b, 0x34, 0x03, 0xfd, 0xfc, 0x02, 0xa9, 0xd3, 0xbb, 0x9e, 0xb8, 0x1d,
+ 0xd3, 0xb6, 0x26, 0x16, 0xf2, 0x3c, 0x58, 0x5a, 0x1e, 0xf4, 0xbe, 0x98, 0x9d, 0xa0, 0x55, 0x66,
+ 0x1d, 0x56, 0x0a, 0xe2, 0xfa, 0xb0, 0xba, 0xee, 0x03, 0xeb, 0x41, 0xf7, 0xf4, 0x42, 0x08, 0x1c,
+ 0x0f, 0xea, 0xf4, 0xf6, 0x14, 0x17, 0xef, 0x08, 0x36, 0xea, 0xe2, 0xff, 0x08, 0xe8, 0x06, 0xf0,
+ 0xfd, 0x08, 0x0a, 0xfc, 0x42, 0xde, 0x42, 0x42, 0x09, 0x2f, 0x3a, 0xf6, 0xfa, 0xda, 0x05, 0x04,
+ 0x01, 0xf3, 0xf8, 0x1a, 0x2f, 0x2f, 0x30, 0x37, 0xe6, 0xf0, 0x25, 0x03, 0xe1, 0x49, 0x44, 0x28,
+ 0xfc, 0xcf, 0xce, 0xf0, 0x98, 0x81, 0x04, 0xfc, 0xdd, 0xef, 0x20, 0xf5, 0x0a, 0xf6, 0xcf, 0xf1,
+ 0xda, 0x0d, 0xf4, 0x04, 0x17, 0xfe, 0x11, 0xff, 0xc2, 0xc0, 0xff, 0xff, 0x8d, 0x56, 0xff, 0xff,
+ 0x3e, 0x1a, 0x00, 0x00, 0x10, 0xdb, 0xff, 0xff, 0x0f, 0xf4, 0xfe, 0xff, 0xc6, 0x89, 0xff, 0xff,
+ 0x22, 0xe9, 0x00, 0x00, 0x15, 0x34, 0x00, 0x00, 0xda, 0x4d, 0x00, 0x00, 0x15, 0x93, 0xff, 0xff,
+ 0x54, 0xde, 0x00, 0x00, 0x79, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0c, 0x00,
- 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x12, 0x00, 0x4a, 0x88, 0x7a, 0x3c, 0x80, 0xff, 0xff, 0xff,
- 0x84, 0xc9, 0x4f, 0x3d, 0x80, 0xff, 0xff, 0xff, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00,
+ 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x12, 0x00, 0x3d, 0x08, 0x7e, 0x3c, 0x80, 0xff, 0xff, 0xff,
+ 0x77, 0x36, 0x2b, 0x3d, 0x80, 0xff, 0xff, 0xff, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
- 0x98, 0x00, 0x00, 0x00, 0x30, 0x08, 0x00, 0x00, 0x17, 0x0d, 0x97, 0x3c, 0xb3, 0xb6, 0x76, 0x3c,
- 0x9c, 0xd3, 0x74, 0x3c, 0xd4, 0x4b, 0x50, 0x3c, 0x49, 0x9f, 0x65, 0x3c, 0x45, 0x76, 0x8f, 0x3c,
- 0x80, 0x2d, 0x59, 0x3c, 0xdd, 0xad, 0x6f, 0x3c, 0x58, 0x29, 0x41, 0x3c, 0x06, 0xe5, 0x24, 0x3c,
- 0x84, 0x06, 0x44, 0x3c, 0x18, 0xd7, 0x2d, 0x3c, 0x31, 0xfe, 0x4d, 0x3c, 0x7e, 0x98, 0x1b, 0x3c,
- 0x30, 0x2e, 0x7e, 0x3c, 0xdb, 0x62, 0x5b, 0x3c, 0x0a, 0x81, 0x45, 0x3c, 0xbe, 0x81, 0x26, 0x3c,
- 0xf4, 0xe3, 0xd9, 0xf9, 0xea, 0x10, 0x21, 0xfc, 0x02, 0xde, 0x01, 0x22, 0xe4, 0x0e, 0x1a, 0xf6,
- 0x14, 0x30, 0x03, 0xea, 0xa0, 0xfa, 0xf1, 0xe7, 0xdb, 0x24, 0xec, 0xfa, 0x16, 0x08, 0xd8, 0xed,
- 0xee, 0xe9, 0x1e, 0xfc, 0x11, 0x1e, 0xf7, 0xfd, 0xf7, 0xf5, 0x19, 0x07, 0xc9, 0xc9, 0x39, 0x46,
- 0xdd, 0x1a, 0xf4, 0xfe, 0x17, 0x0e, 0x30, 0x08, 0xc0, 0x17, 0xe0, 0xfc, 0x37, 0x01, 0x0a, 0xd2,
- 0x28, 0x1f, 0x13, 0xf0, 0xf9, 0x7f, 0xdb, 0xee, 0xdd, 0xce, 0xfe, 0x1a, 0x23, 0x1f, 0x14, 0x29,
- 0x2c, 0xe9, 0xf5, 0x36, 0x05, 0xfd, 0x0c, 0xe6, 0xd4, 0xfe, 0xf9, 0x0c, 0xd8, 0xba, 0x2b, 0x28,
- 0xe1, 0x26, 0x17, 0x2c, 0x18, 0xf3, 0xf5, 0x0a, 0xe0, 0x0a, 0xd5, 0xd1, 0xf0, 0x07, 0x18, 0x11,
- 0x07, 0xfd, 0xea, 0x1a, 0x1d, 0x14, 0xf7, 0x0d, 0xcd, 0xc6, 0x06, 0xd8, 0xdc, 0xfc, 0xbf, 0xca,
- 0xe5, 0x10, 0x08, 0xf5, 0x04, 0xe8, 0x21, 0x1b, 0x18, 0x0a, 0x0e, 0xdb, 0xd3, 0xd0, 0xcc, 0xd0,
- 0xf0, 0xdd, 0x13, 0xe1, 0xe7, 0x28, 0x81, 0xb9, 0x13, 0xe9, 0xa3, 0xd4, 0xdf, 0x06, 0x49, 0x1a,
- 0xda, 0xdd, 0x1a, 0x19, 0xec, 0x00, 0x12, 0xfa, 0x55, 0x25, 0x13, 0x03, 0xd0, 0xed, 0x9f, 0xca,
- 0x20, 0xd8, 0xf9, 0x10, 0x44, 0x36, 0x0e, 0x2c, 0x36, 0xef, 0xd7, 0x19, 0x04, 0x1a, 0xf6, 0xc0,
- 0xfa, 0xa0, 0xad, 0x18, 0xe6, 0xd8, 0xff, 0xac, 0x11, 0xfd, 0xe9, 0xd8, 0xff, 0xd1, 0xfa, 0x0f,
- 0xf9, 0x11, 0xa0, 0xf1, 0xea, 0x93, 0xee, 0x34, 0xef, 0x04, 0xf7, 0x01, 0xfc, 0xfb, 0x19, 0x4c,
- 0x21, 0x17, 0x11, 0x47, 0xe3, 0x00, 0x08, 0x0b, 0x17, 0xfc, 0x3c, 0x1a, 0x11, 0x0d, 0x00, 0xe1,
- 0xcc, 0xda, 0x0f, 0x2c, 0xe5, 0xad, 0xe6, 0xd2, 0x23, 0x19, 0x0e, 0x28, 0xc3, 0xdb, 0xf2, 0xdb,
- 0x0e, 0x2c, 0x02, 0x2b, 0xe9, 0xf7, 0x2f, 0x40, 0x01, 0xfd, 0x3e, 0x0e, 0xd1, 0xbf, 0x0a, 0x25,
- 0x15, 0x20, 0xd1, 0xd0, 0xff, 0xb9, 0xcc, 0xfb, 0xc4, 0x00, 0xff, 0x10, 0x2d, 0x4b, 0x4e, 0x02,
- 0x31, 0x0f, 0xd7, 0x29, 0xc5, 0xe5, 0xf0, 0xf7, 0x0b, 0xf9, 0xf5, 0x2c, 0x08, 0xd9, 0x32, 0x04,
- 0x0a, 0x04, 0x81, 0xe1, 0xe3, 0x2c, 0xce, 0xec, 0x16, 0xdf, 0x22, 0xd2, 0x03, 0x05, 0xf1, 0x1b,
- 0x11, 0xfa, 0x2c, 0x17, 0x23, 0x0e, 0x0f, 0x0a, 0xfc, 0xe6, 0xdf, 0xf9, 0x09, 0x16, 0x68, 0x53,
- 0x69, 0x42, 0xf3, 0x01, 0xda, 0xf2, 0x1b, 0xfc, 0x12, 0x1a, 0x32, 0x2a, 0x50, 0x25, 0x1d, 0x24,
- 0x63, 0x50, 0x46, 0x41, 0x26, 0x56, 0x13, 0xed, 0x50, 0x20, 0xf2, 0x29, 0x27, 0x12, 0xf7, 0xd1,
- 0xf2, 0x0a, 0x14, 0xfb, 0xeb, 0xc8, 0xd5, 0xef, 0xe3, 0x14, 0x07, 0x01, 0xbd, 0xa2, 0xd6, 0xea,
- 0xfb, 0x03, 0x11, 0xba, 0x38, 0x14, 0xbe, 0xd6, 0x20, 0x81, 0x94, 0xd4, 0xd1, 0xf6, 0x02, 0x29,
- 0xe0, 0xee, 0x21, 0xf0, 0xf0, 0x32, 0x68, 0x3b, 0xfd, 0x34, 0xf9, 0xae, 0x06, 0x11, 0xb8, 0xd5,
- 0xd4, 0xd0, 0xcf, 0xee, 0x04, 0xd3, 0x01, 0x39, 0x15, 0xe2, 0xf4, 0xff, 0x09, 0xfb, 0xf4, 0xeb,
- 0xaa, 0xff, 0x42, 0x15, 0x1d, 0x0a, 0x81, 0xe1, 0x02, 0x17, 0x01, 0x24, 0xed, 0xc3, 0xf3, 0xad,
- 0xce, 0xbd, 0xfa, 0xe2, 0xf5, 0xee, 0xed, 0x95, 0x08, 0xe4, 0xde, 0x0e, 0x2a, 0x2b, 0x1a, 0xcb,
- 0xe5, 0xae, 0x9e, 0xe2, 0x14, 0x1b, 0xdb, 0x02, 0xf9, 0x0d, 0x02, 0xf5, 0xcd, 0xed, 0x0f, 0x16,
- 0x2b, 0x02, 0xfe, 0x03, 0xfc, 0xfd, 0xed, 0x03, 0xf8, 0xee, 0x31, 0x2a, 0x0b, 0xd9, 0xc8, 0xba,
- 0xc0, 0xf9, 0xb6, 0xce, 0xec, 0x0a, 0x39, 0x0c, 0xee, 0xf4, 0x1b, 0x3f, 0x11, 0xd8, 0x0b, 0x21,
- 0x1f, 0xf6, 0xcf, 0xe6, 0xd3, 0xa1, 0xc9, 0xc6, 0xbb, 0xdb, 0x1d, 0xe2, 0x9f, 0xd7, 0xf2, 0xba,
- 0xc0, 0xd6, 0xaf, 0xef, 0x00, 0xf0, 0xde, 0x1f, 0x32, 0xce, 0xc1, 0xda, 0xc5, 0x29, 0x0e, 0x03,
- 0x1c, 0xf8, 0x07, 0x13, 0xfb, 0x1f, 0x59, 0x13, 0x04, 0x2f, 0xed, 0xed, 0xe2, 0xf9, 0xfb, 0x03,
- 0xd3, 0xd8, 0xcb, 0x14, 0x4f, 0x25, 0x0b, 0xdb, 0x1c, 0x3a, 0xd6, 0xfa, 0xd6, 0xe9, 0xd1, 0xde,
- 0x44, 0x43, 0x07, 0x28, 0xf3, 0xe7, 0x24, 0x00, 0xca, 0x04, 0xf1, 0x14, 0x2d, 0xe1, 0x10, 0xe0,
- 0x06, 0xf6, 0x8c, 0x13, 0xe0, 0xba, 0xba, 0xe5, 0xfb, 0x2f, 0x44, 0x46, 0x30, 0x04, 0x46, 0x79,
- 0x2a, 0x81, 0xf0, 0xee, 0xf6, 0xf4, 0x0a, 0xfb, 0x06, 0x06, 0xe9, 0x37, 0x06, 0xe3, 0x4d, 0xfd,
- 0xe0, 0x11, 0x05, 0xe6, 0xad, 0xff, 0xda, 0xe6, 0xe3, 0x08, 0xf8, 0xd2, 0xf2, 0x37, 0xdc, 0x20,
- 0x09, 0xf5, 0x0e, 0xff, 0xc9, 0xf3, 0xdf, 0x12, 0xe8, 0x1b, 0x4f, 0x21, 0x29, 0x3c, 0x1a, 0x94,
- 0xa3, 0x16, 0xec, 0xfb, 0xdf, 0x03, 0x2a, 0xf3, 0x0d, 0xea, 0x96, 0x94, 0x9c, 0x81, 0xf9, 0x1b,
- 0x1f, 0x21, 0x34, 0xcb, 0xb6, 0xb9, 0x0d, 0x0a, 0x30, 0x26, 0x29, 0x3c, 0x0b, 0xcf, 0xf2, 0x07,
- 0xe9, 0x27, 0xed, 0xdc, 0xd1, 0x10, 0x14, 0xda, 0x06, 0xec, 0x04, 0xde, 0x06, 0x17, 0x4f, 0xf7,
- 0x0e, 0xf3, 0xee, 0xd9, 0xee, 0xe2, 0xe6, 0xce, 0xdf, 0xe8, 0x0f, 0xee, 0x05, 0xe9, 0xfc, 0xe2,
- 0x0b, 0xf7, 0x04, 0x3e, 0x09, 0x0f, 0x18, 0x05, 0x17, 0xee, 0x08, 0xc3, 0x33, 0x07, 0x27, 0xcd,
- 0x1c, 0x19, 0xe9, 0x28, 0xf1, 0xfe, 0x02, 0x05, 0x15, 0xc4, 0xe3, 0xfa, 0xbe, 0x0f, 0x0a, 0x33,
- 0x00, 0x11, 0xf4, 0xdf, 0x23, 0x09, 0xf9, 0xe2, 0x15, 0x18, 0xe7, 0xef, 0xf7, 0x27, 0xf6, 0x81,
- 0x16, 0x06, 0x11, 0x2d, 0x09, 0x04, 0x3a, 0xf2, 0x1f, 0x23, 0xcd, 0xc7, 0xee, 0xea, 0x39, 0x09,
- 0xdc, 0x12, 0xf3, 0xed, 0xce, 0xec, 0x37, 0x3f, 0x1c, 0xd2, 0xdd, 0x3d, 0xfc, 0x34, 0x08, 0x2a,
- 0xc7, 0x16, 0x1b, 0x19, 0xf9, 0x05, 0xbb, 0xfd, 0xd2, 0xd4, 0x29, 0x11, 0x0a, 0x18, 0x27, 0x12,
- 0xd4, 0xdb, 0xef, 0x08, 0xe4, 0x06, 0x37, 0xc0, 0xe0, 0x2e, 0xfb, 0x09, 0xf0, 0x1d, 0x20, 0xc0,
- 0xfe, 0x0e, 0x0a, 0x2b, 0x0d, 0xf7, 0xd4, 0x03, 0x5b, 0x31, 0xd3, 0x08, 0x19, 0x15, 0x0e, 0xfc,
- 0xaa, 0xd1, 0x3e, 0x08, 0x3a, 0xb9, 0xea, 0x18, 0xe7, 0x1c, 0x00, 0x35, 0xf4, 0xef, 0xf2, 0xcf,
- 0x95, 0xe8, 0x19, 0x11, 0xf7, 0x21, 0x42, 0x1e, 0xdc, 0xab, 0xb6, 0x13, 0x10, 0x1e, 0x0b, 0xf8,
- 0x13, 0xcd, 0xd8, 0xfa, 0x0e, 0x0f, 0xdb, 0xc6, 0x24, 0x33, 0x2e, 0xbb, 0xc2, 0xad, 0xc6, 0xc9,
- 0xcb, 0x0f, 0x22, 0x0e, 0xad, 0xfb, 0xde, 0x0e, 0x25, 0x17, 0xfe, 0x27, 0x1d, 0x4e, 0x1d, 0x2a,
- 0xd8, 0x2d, 0xe1, 0xcd, 0xca, 0xff, 0xf9, 0x24, 0x7f, 0x39, 0x10, 0x2e, 0x02, 0xfa, 0xfb, 0xd2,
- 0x05, 0xef, 0xf2, 0x3a, 0x24, 0xe2, 0x35, 0x3a, 0xfe, 0x0b, 0xc9, 0x06, 0x0e, 0xec, 0x25, 0x49,
- 0x48, 0x14, 0xf0, 0x3d, 0xba, 0xd9, 0x36, 0xf4, 0xdf, 0xd8, 0xd7, 0xe5, 0xf7, 0x1c, 0xfa, 0xf1,
- 0xd4, 0x01, 0x32, 0x00, 0x18, 0xeb, 0x04, 0xfd, 0x14, 0x29, 0xea, 0x10, 0xd2, 0x12, 0xf4, 0xe6,
- 0xc0, 0xcc, 0xec, 0xf0, 0x1f, 0xf9, 0xe9, 0x03, 0xe4, 0xf0, 0x01, 0xf5, 0xfa, 0xad, 0x63, 0x2b,
- 0x2f, 0x2b, 0x03, 0xff, 0x28, 0xea, 0xdd, 0xf2, 0x0d, 0xd0, 0x29, 0x2d, 0x52, 0xf0, 0xbb, 0xdf,
- 0xf4, 0xbb, 0xf7, 0xcd, 0xb2, 0x9a, 0x0d, 0x02, 0xef, 0xdf, 0x03, 0x5b, 0x0b, 0x21, 0xec, 0x44,
- 0x53, 0xeb, 0x1d, 0x00, 0xe1, 0xdb, 0xe9, 0xf6, 0x1b, 0x0c, 0x02, 0x26, 0x42, 0xe0, 0x57, 0x49,
- 0x26, 0x69, 0xe2, 0xcd, 0x10, 0x2a, 0xf3, 0x01, 0x69, 0x41, 0xfe, 0x3c, 0x2f, 0xca, 0x0a, 0xc4,
- 0x0a, 0x1b, 0x1a, 0xda, 0xe8, 0x2b, 0x6f, 0x60, 0x77, 0xb0, 0xda, 0x17, 0x81, 0xd4, 0xee, 0xc6,
- 0xda, 0xd8, 0x0d, 0xea, 0xcf, 0x05, 0x17, 0x2c, 0xef, 0x32, 0x46, 0xeb, 0x11, 0xcb, 0xe6, 0xfe,
- 0x16, 0x00, 0x2a, 0x3a, 0x0d, 0x04, 0xf8, 0x05, 0x18, 0x59, 0xc3, 0xfa, 0x28, 0x19, 0x03, 0xf8,
- 0x0b, 0x0e, 0xe5, 0xf7, 0xf7, 0x14, 0x7f, 0x3c, 0x08, 0xe6, 0xfd, 0x25, 0x04, 0xce, 0xee, 0xf9,
- 0xb9, 0xc4, 0x2d, 0x13, 0xf4, 0x57, 0x32, 0x29, 0x06, 0x15, 0x1d, 0xc7, 0x18, 0xf3, 0x01, 0x05,
- 0x30, 0x9b, 0x12, 0xf9, 0x1c, 0x04, 0xbe, 0xea, 0xff, 0xce, 0xfd, 0x16, 0x00, 0xc4, 0x0e, 0x26,
- 0xd2, 0xc4, 0xed, 0xf6, 0xda, 0xb9, 0x03, 0xdf, 0x16, 0x50, 0x0c, 0xc9, 0xfc, 0x0d, 0x1c, 0x07,
- 0x21, 0xc4, 0xd6, 0x58, 0x09, 0xe5, 0x33, 0xa3, 0xc8, 0xfc, 0x07, 0x46, 0xf0, 0x1d, 0x0f, 0xf9,
- 0xd9, 0x51, 0x17, 0xd6, 0x14, 0x38, 0xdc, 0x0f, 0x14, 0x11, 0x69, 0x1f, 0x12, 0x26, 0xfc, 0xa1,
- 0x17, 0x12, 0x40, 0x6f, 0x10, 0xe2, 0xe0, 0x0b, 0x19, 0x0c, 0xe0, 0xdf, 0x12, 0x3a, 0xf9, 0x11,
- 0xfc, 0x53, 0x8f, 0x36, 0x61, 0x16, 0x16, 0x00, 0x12, 0x13, 0xa1, 0x13, 0x16, 0xe0, 0x46, 0xd8,
- 0xe9, 0xf3, 0x08, 0x27, 0x29, 0x0c, 0x15, 0x10, 0xda, 0x1c, 0xe3, 0xea, 0xb7, 0x3a, 0xad, 0xcd,
- 0x03, 0xc9, 0x06, 0xda, 0x34, 0x35, 0x31, 0x72, 0xf9, 0x5f, 0xda, 0x37, 0x04, 0xfd, 0xed, 0xe8,
- 0xdc, 0xcc, 0x1f, 0xda, 0x08, 0xfe, 0xda, 0xe8, 0x22, 0xfc, 0x3d, 0x06, 0x04, 0x23, 0xbe, 0x3b,
- 0xac, 0x42, 0xdd, 0x15, 0x7f, 0x0e, 0x38, 0xf9, 0x1e, 0xf7, 0x2d, 0x0c, 0xcd, 0x3b, 0x0f, 0xb9,
- 0xdb, 0x0e, 0x24, 0x0d, 0x12, 0x09, 0xfe, 0x11, 0x2f, 0xfb, 0x61, 0x3c, 0xa6, 0x2d, 0x08, 0x53,
- 0xe3, 0x0d, 0xdb, 0x41, 0x19, 0x25, 0x0a, 0x4e, 0xe8, 0x1a, 0x3c, 0xe7, 0x39, 0x47, 0x15, 0x29,
- 0x09, 0x12, 0xc4, 0xe8, 0x23, 0x4a, 0x5d, 0x0d, 0x33, 0x0a, 0xba, 0x1b, 0x19, 0x2c, 0x03, 0xe7,
- 0x15, 0x0e, 0xe8, 0xa8, 0xf9, 0xe0, 0xe3, 0xff, 0xfe, 0xe2, 0x25, 0x13, 0xfa, 0x1c, 0x00, 0x02,
- 0xf8, 0x14, 0x03, 0x1a, 0x00, 0x33, 0x75, 0x58, 0xdb, 0xef, 0x28, 0xde, 0xe8, 0x09, 0xea, 0xbb,
- 0xce, 0x1a, 0x01, 0x57, 0x05, 0x17, 0x2c, 0x14, 0x1e, 0xc9, 0x30, 0xf2, 0xc0, 0x30, 0x20, 0xdc,
- 0x0e, 0xf8, 0x6f, 0x1e, 0x41, 0x26, 0xcf, 0x7f, 0xde, 0x14, 0x18, 0x2a, 0xbf, 0xdd, 0xf9, 0xac,
- 0xed, 0x22, 0x0c, 0xba, 0xda, 0x85, 0xda, 0x3b, 0xa9, 0xdc, 0x16, 0x0f, 0xa8, 0xba, 0xed, 0x5a,
- 0x15, 0xaf, 0x33, 0x60, 0xe5, 0x0e, 0x2d, 0x3b, 0xd9, 0x1e, 0xc4, 0xc6, 0x24, 0xc3, 0x33, 0x50,
- 0xb1, 0x03, 0x56, 0x96, 0x22, 0x1a, 0xe4, 0x0b, 0xfd, 0x02, 0xdf, 0xce, 0x38, 0x01, 0xca, 0x29,
- 0xf7, 0x05, 0xc6, 0x15, 0x3a, 0x59, 0x55, 0x52, 0x26, 0x0b, 0x29, 0xac, 0x10, 0x10, 0xa0, 0xce,
- 0xbf, 0xf2, 0x10, 0x46, 0xf3, 0xe4, 0x11, 0x40, 0xe8, 0x19, 0x0c, 0xf9, 0xdc, 0x54, 0xd9, 0xf6,
- 0x29, 0xca, 0xb8, 0x1d, 0xee, 0xd3, 0xb8, 0x1e, 0x4a, 0x19, 0x28, 0xc9, 0xce, 0xca, 0x29, 0xc1,
- 0x27, 0x3a, 0x81, 0xee, 0x0d, 0xf1, 0xc4, 0xf7, 0x04, 0x1f, 0xb7, 0xe7, 0xf4, 0xf9, 0x1e, 0x28,
- 0x04, 0xf4, 0x06, 0xcd, 0xfa, 0x09, 0x03, 0xdd, 0xf8, 0xf1, 0xfd, 0x09, 0x32, 0xf2, 0x05, 0x30,
- 0xf7, 0x0b, 0x3f, 0xe3, 0x06, 0xe4, 0xe0, 0x37, 0x30, 0x3d, 0xe3, 0xf5, 0xe9, 0xea, 0x19, 0x30,
- 0xcc, 0xd9, 0x14, 0x05, 0xdb, 0x2f, 0xe4, 0xde, 0xf4, 0xec, 0xe3, 0xf1, 0x16, 0x20, 0xd3, 0xf4,
- 0xe4, 0xe1, 0x23, 0x20, 0xfc, 0x20, 0x3c, 0x7f, 0x42, 0x31, 0x6b, 0xf6, 0xed, 0x05, 0xe3, 0xea,
- 0x0a, 0x06, 0x4c, 0x1c, 0x03, 0x0f, 0xfd, 0xe7, 0xda, 0x0e, 0xfb, 0xf8, 0xe4, 0x1e, 0x0f, 0x03,
- 0x12, 0x00, 0x24, 0x0d, 0x22, 0x04, 0xdf, 0x0a, 0xee, 0x13, 0x22, 0x03, 0xf6, 0xe3, 0xe9, 0xe7,
- 0x11, 0x1e, 0x48, 0x0b, 0x43, 0xec, 0xd2, 0xda, 0xbd, 0x16, 0x09, 0xfd, 0xf7, 0xbe, 0xd1, 0xe7,
- 0x11, 0xe9, 0x0d, 0x13, 0x31, 0x0f, 0x0b, 0x0b, 0xe5, 0xf8, 0xf8, 0xd8, 0xbb, 0x02, 0x1f, 0x40,
- 0xe5, 0xa7, 0x00, 0x11, 0xf2, 0xff, 0x07, 0x2a, 0xa2, 0xc8, 0x11, 0xbf, 0xff, 0x01, 0x23, 0x17,
- 0x05, 0xe1, 0x13, 0x06, 0x1a, 0x32, 0xed, 0x27, 0x0f, 0xe2, 0x1e, 0x1a, 0x18, 0x17, 0xf1, 0x14,
- 0xf7, 0x06, 0xf6, 0x25, 0xeb, 0xec, 0x7f, 0x0e, 0x10, 0x37, 0x97, 0xf9, 0x46, 0x2a, 0xda, 0xd3,
- 0x37, 0x2f, 0x00, 0x2f, 0x10, 0xdb, 0x0a, 0xf5, 0x2c, 0x26, 0xf8, 0xfa, 0xeb, 0x07, 0xb4, 0x21,
- 0x45, 0xae, 0xf3, 0x14, 0x02, 0x09, 0xf2, 0x94, 0xda, 0xe4, 0xdf, 0x0d, 0xfa, 0xd1, 0x12, 0xf5,
- 0x01, 0xd3, 0xf7, 0x10, 0x26, 0x04, 0x24, 0x01, 0xdc, 0xe0, 0xf0, 0x09, 0xff, 0xdb, 0x17, 0x2b,
- 0x07, 0xf0, 0xce, 0xc0, 0xda, 0x81, 0xfd, 0x02, 0x00, 0x2e, 0xf3, 0xc6, 0x03, 0x35, 0x9d, 0x0b,
- 0xef, 0x22, 0xf4, 0xe5, 0xdf, 0xca, 0xac, 0x0f, 0xc9, 0xc6, 0x21, 0xe7, 0xc1, 0xef, 0xaf, 0xbf,
- 0x0e, 0x23, 0x01, 0xca, 0x3f, 0x3a, 0x4d, 0xee, 0x00, 0x30, 0x20, 0x32, 0xdf, 0x1a, 0x1e, 0x32,
- 0x30, 0xe4, 0x03, 0xa7, 0xbd, 0xfd, 0xe3, 0xdc, 0x01, 0x3c, 0x48, 0x00, 0x07, 0x32, 0x2a, 0x00,
- 0xde, 0xf9, 0xa7, 0x00, 0x21, 0xf4, 0xec, 0xcd, 0x10, 0x1f, 0xbe, 0x1b, 0xeb, 0x2e, 0x11, 0xcd,
- 0xf0, 0x10, 0x05, 0xdb, 0xd7, 0xbf, 0x14, 0xe4, 0x10, 0xe2, 0xca, 0x00, 0xfc, 0x9f, 0xbe, 0xe5,
- 0x2d, 0x29, 0x17, 0xf3, 0xfd, 0x1a, 0x0e, 0x09, 0xdf, 0x19, 0xb1, 0xb9, 0x00, 0xe9, 0xae, 0xa1,
- 0xb6, 0x0f, 0xfb, 0xec, 0x0c, 0x31, 0x18, 0x36, 0x2b, 0x5a, 0x1c, 0x12, 0x20, 0x13, 0x00, 0xf9,
- 0x10, 0xc5, 0xa8, 0x46, 0x31, 0xdb, 0xd5, 0x21, 0x3f, 0x6c, 0x2e, 0xc1, 0xa4, 0xd0, 0x03, 0x19,
- 0x43, 0x1b, 0xee, 0xa4, 0xf5, 0xd5, 0x51, 0x60, 0xe4, 0x0f, 0x1a, 0xf6, 0x0e, 0xf3, 0x57, 0xb0,
- 0xd7, 0x38, 0xc2, 0xcd, 0xf3, 0xe9, 0x07, 0xeb, 0x27, 0x20, 0x1e, 0x21, 0xe0, 0xea, 0x14, 0x14,
- 0xf0, 0x15, 0x09, 0x1b, 0x01, 0xde, 0x11, 0xd5, 0x1c, 0xf5, 0x43, 0x39, 0xb1, 0x21, 0x73, 0x69,
- 0xf8, 0x11, 0xf2, 0x1a, 0x7f, 0xbb, 0x00, 0xdc, 0xa8, 0x17, 0x00, 0x00, 0xb0, 0x09, 0xfe, 0xff,
- 0xc9, 0x62, 0x00, 0x00, 0x05, 0xa2, 0x00, 0x00, 0xf2, 0x14, 0xfd, 0xff, 0xd7, 0xef, 0xff, 0xff,
- 0x2c, 0x3c, 0xff, 0xff, 0x02, 0x31, 0x00, 0x00, 0x44, 0x20, 0x00, 0x00, 0xe5, 0x52, 0x00, 0x00,
- 0x69, 0x7b, 0x00, 0x00, 0x42, 0x7f, 0x01, 0x00, 0xb9, 0x73, 0x02, 0x00, 0xa4, 0xfa, 0xfe, 0xff,
- 0xe9, 0xeb, 0x00, 0x00, 0x89, 0xdd, 0xff, 0xff, 0xdd, 0xb7, 0xfe, 0xff, 0x8b, 0x7c, 0x00, 0x00,
+ 0x98, 0x00, 0x00, 0x00, 0x30, 0x08, 0x00, 0x00, 0x82, 0x18, 0x7f, 0x3c, 0x20, 0x98, 0x31, 0x3c,
+ 0x60, 0x7d, 0x25, 0x3c, 0xb5, 0x9a, 0x0a, 0x3c, 0x41, 0xf4, 0x5b, 0x3c, 0x64, 0xaa, 0x09, 0x3c,
+ 0xc9, 0xee, 0x14, 0x3c, 0xfd, 0x49, 0x1f, 0x3c, 0xf0, 0xb7, 0xf4, 0x3b, 0x49, 0x12, 0x15, 0x3c,
+ 0xe2, 0x4e, 0x06, 0x3c, 0xa8, 0x85, 0x2b, 0x3c, 0x8c, 0xe0, 0x0b, 0x3c, 0x5d, 0x6b, 0x37, 0x3c,
+ 0x23, 0xc5, 0x19, 0x3c, 0x5d, 0xc7, 0x21, 0x3c, 0x5b, 0x3b, 0x2e, 0x3c, 0xff, 0x23, 0x43, 0x3c,
+ 0xf0, 0xf8, 0xf0, 0xdb, 0xed, 0xf0, 0xcd, 0xd1, 0x0e, 0xe5, 0xe5, 0x09, 0x09, 0xdc, 0xe5, 0x24,
+ 0xf4, 0x07, 0x0a, 0x1c, 0x23, 0xc4, 0xfc, 0x13, 0xd5, 0xa1, 0xf4, 0xee, 0xdd, 0x04, 0x2b, 0xda,
+ 0xd2, 0xba, 0xe0, 0x00, 0x05, 0xda, 0xb5, 0x47, 0x7f, 0x0b, 0xd8, 0xd2, 0xcd, 0x00, 0xf3, 0x00,
+ 0x02, 0xe2, 0xf4, 0x00, 0xd9, 0xde, 0xfc, 0x00, 0x42, 0xe7, 0xf4, 0xe2, 0xf3, 0x0d, 0x18, 0x0e,
+ 0x07, 0x03, 0x06, 0x17, 0xdf, 0x2c, 0x1a, 0x03, 0x02, 0x0c, 0x13, 0xf0, 0xca, 0xb8, 0xf9, 0x00,
+ 0x2f, 0xf1, 0xfa, 0x00, 0x97, 0xf3, 0x04, 0xcf, 0xe5, 0xf3, 0xdf, 0x33, 0x18, 0xfa, 0xdf, 0xc7,
+ 0xe0, 0xd7, 0xe5, 0x06, 0xe3, 0xd8, 0x1e, 0x2d, 0x10, 0xf4, 0x13, 0x0d, 0x0c, 0x0d, 0x0b, 0x0a,
+ 0x3e, 0x22, 0x24, 0x11, 0x09, 0x12, 0xcf, 0x1f, 0x17, 0xfd, 0xfe, 0xec, 0x1f, 0xf3, 0x70, 0x29,
+ 0x0b, 0x35, 0xf4, 0xe5, 0xe9, 0xc8, 0xc8, 0x04, 0xef, 0x1d, 0x02, 0x35, 0x79, 0xfa, 0x38, 0x47,
+ 0x1b, 0x23, 0xe7, 0xbb, 0x9a, 0x93, 0xe4, 0x4d, 0xdd, 0x27, 0x3e, 0xcb, 0x11, 0xef, 0xfe, 0xfd,
+ 0x3a, 0x0a, 0xf5, 0x05, 0x0f, 0xfc, 0xdf, 0xcf, 0xee, 0x19, 0xea, 0x93, 0xed, 0xde, 0xca, 0xe8,
+ 0x2f, 0xd0, 0xf1, 0xfe, 0xa8, 0x40, 0x20, 0xc6, 0xd8, 0x0b, 0xdc, 0xe5, 0xed, 0xca, 0x1d, 0x3a,
+ 0x00, 0xf1, 0xcd, 0x0f, 0xe4, 0xde, 0x6e, 0x7f, 0x1b, 0x08, 0x6c, 0x29, 0xde, 0xf5, 0xd7, 0xf9,
+ 0xf7, 0xf6, 0xd3, 0xe8, 0x11, 0xcf, 0xdc, 0x14, 0x28, 0x61, 0x03, 0x22, 0x4d, 0x11, 0x05, 0x28,
+ 0x4d, 0xb7, 0x93, 0xf6, 0x81, 0xbf, 0xd0, 0xb6, 0xeb, 0x18, 0x60, 0x54, 0xf8, 0x54, 0x1b, 0xe5,
+ 0x14, 0xe2, 0xd6, 0x17, 0xf4, 0xea, 0x0d, 0x25, 0x15, 0xcd, 0xe1, 0xf6, 0x03, 0x9d, 0x97, 0x2b,
+ 0xf5, 0xfc, 0x05, 0xf7, 0xcb, 0xee, 0x21, 0x08, 0xd6, 0xbe, 0xae, 0xec, 0xc1, 0xde, 0xf7, 0xcb,
+ 0x29, 0xe3, 0xbb, 0x15, 0xef, 0xda, 0x92, 0xb2, 0x0a, 0xf9, 0xe0, 0x21, 0x1b, 0xee, 0x04, 0xd2,
+ 0xda, 0xda, 0x0d, 0xd7, 0x99, 0xc4, 0x11, 0xf3, 0x1d, 0x10, 0x1b, 0x62, 0xbd, 0x36, 0x33, 0x0d,
+ 0xec, 0xed, 0xf3, 0x0c, 0x2f, 0x0c, 0x53, 0x59, 0xe6, 0x29, 0x28, 0xd8, 0xd1, 0xfe, 0xd6, 0xad,
+ 0xcd, 0x14, 0xc5, 0xef, 0xfb, 0xd3, 0xcf, 0xbc, 0xf1, 0xdb, 0xe5, 0x00, 0x00, 0xc9, 0x39, 0xef,
+ 0x24, 0x0e, 0x13, 0xed, 0x20, 0x05, 0xb1, 0x81, 0xff, 0xcc, 0xa7, 0xac, 0x01, 0xe2, 0x06, 0xc1,
+ 0x0a, 0x16, 0x1c, 0x6b, 0x7e, 0xe8, 0xff, 0x05, 0xb1, 0xf5, 0x5c, 0xf8, 0xc6, 0xd4, 0xab, 0x15,
+ 0x01, 0xc7, 0xf6, 0xbb, 0xc4, 0xe3, 0xdd, 0xf5, 0x0d, 0xf8, 0xe7, 0xde, 0xde, 0x17, 0x44, 0x2c,
+ 0xfd, 0x28, 0xe9, 0xb0, 0xd2, 0x29, 0x07, 0xd8, 0xca, 0xe9, 0xfc, 0xdd, 0xf3, 0x33, 0x46, 0xfd,
+ 0x7e, 0x28, 0x29, 0x0c, 0xdf, 0x40, 0x3e, 0x10, 0x5b, 0x19, 0x2a, 0xfe, 0xb5, 0xc4, 0x0f, 0xe0,
+ 0xeb, 0x29, 0x3a, 0x45, 0x20, 0xfc, 0x0f, 0x47, 0x41, 0x49, 0x30, 0x2e, 0x0b, 0xfe, 0xe5, 0x18,
+ 0x08, 0x06, 0x03, 0xec, 0xd6, 0x04, 0xeb, 0x04, 0xf5, 0xfd, 0x02, 0x2f, 0x32, 0x04, 0x0b, 0x14,
+ 0xf2, 0xe7, 0xfe, 0x31, 0x2c, 0xf3, 0xef, 0xd5, 0x0b, 0x1f, 0xe1, 0x1b, 0xdb, 0xf9, 0x12, 0x05,
+ 0x33, 0xbc, 0xd7, 0x31, 0x38, 0x07, 0xfd, 0xed, 0xcf, 0xd0, 0xa8, 0x11, 0x35, 0xe1, 0x12, 0x05,
+ 0xd8, 0xf4, 0xe9, 0x0c, 0x19, 0x1a, 0xea, 0x19, 0x52, 0x11, 0x38, 0x2c, 0xe1, 0x1f, 0xec, 0xed,
+ 0x35, 0x2e, 0xac, 0xe9, 0x12, 0xeb, 0xf1, 0x11, 0x0f, 0x6b, 0x75, 0x25, 0x7f, 0x61, 0xcd, 0x02,
+ 0x2d, 0xe6, 0x1f, 0x0f, 0xda, 0x1e, 0x03, 0xd3, 0xe8, 0xf6, 0x23, 0xdc, 0xd5, 0x39, 0x06, 0xe4,
+ 0x48, 0x1e, 0xe9, 0x06, 0xfd, 0xf8, 0xec, 0x0a, 0x2e, 0x0c, 0xff, 0x29, 0xd4, 0xd7, 0xd0, 0x2a,
+ 0xfd, 0xb5, 0x01, 0xdc, 0xf0, 0x4f, 0x37, 0xec, 0x3a, 0x17, 0xf4, 0x05, 0xd7, 0xc1, 0xbe, 0xc3,
+ 0xc6, 0xda, 0x4f, 0xc2, 0xcc, 0x51, 0x27, 0x43, 0xfb, 0xf1, 0x1e, 0xb1, 0x8f, 0xa3, 0xef, 0x04,
+ 0xf4, 0xe6, 0xea, 0x08, 0xfa, 0xa6, 0x0d, 0x0f, 0x47, 0x8a, 0xe2, 0x7f, 0xdf, 0x0e, 0x28, 0xfe,
+ 0xf2, 0xf5, 0x5d, 0xf3, 0xc3, 0x16, 0xf9, 0x20, 0xc9, 0xc8, 0xe6, 0x51, 0x1a, 0x9d, 0xf5, 0x6a,
+ 0x04, 0x05, 0xf6, 0x08, 0x18, 0x2a, 0x89, 0x69, 0x52, 0x7e, 0x1b, 0x10, 0x15, 0x3b, 0xd0, 0x86,
+ 0xb6, 0xae, 0xbe, 0xd6, 0x09, 0x5a, 0x5e, 0xcf, 0x84, 0x3c, 0xfe, 0xb9, 0x64, 0x40, 0x02, 0xd2,
+ 0x21, 0x68, 0xed, 0xd9, 0x0a, 0xea, 0xd8, 0x92, 0x96, 0xa7, 0xed, 0x05, 0xd9, 0x1e, 0x23, 0x01,
+ 0xf6, 0x57, 0xfa, 0xff, 0x04, 0x03, 0x28, 0xcb, 0xae, 0xb1, 0xb6, 0xf3, 0xb5, 0x38, 0xef, 0xcd,
+ 0xce, 0xfc, 0x02, 0x31, 0xf0, 0xe5, 0xea, 0x9a, 0xba, 0x34, 0x20, 0xf2, 0xca, 0x1c, 0xd0, 0xeb,
+ 0x34, 0x11, 0x07, 0xfe, 0x43, 0xac, 0x81, 0x06, 0xfa, 0xe6, 0x1f, 0x39, 0x2d, 0x35, 0x05, 0x52,
+ 0x13, 0x20, 0xd9, 0xd8, 0xfc, 0x0f, 0x10, 0x2d, 0x05, 0x0c, 0x6d, 0x1a, 0xf8, 0xcb, 0xf6, 0xf1,
+ 0x2e, 0x37, 0xd9, 0xf4, 0x3d, 0xeb, 0xf5, 0xfd, 0x02, 0xe9, 0xe9, 0xb7, 0x93, 0x08, 0xde, 0x1d,
+ 0x21, 0x1a, 0xe2, 0xe1, 0xf6, 0x31, 0x12, 0x04, 0xfd, 0xed, 0xee, 0x15, 0x2c, 0xd8, 0xd5, 0x0f,
+ 0x42, 0x17, 0x42, 0x1f, 0xf3, 0xff, 0x0c, 0x30, 0xda, 0xfd, 0x45, 0x14, 0x0d, 0xdb, 0x03, 0xbb,
+ 0x18, 0x1a, 0xfd, 0x07, 0x3f, 0x47, 0x39, 0x3a, 0x38, 0x0a, 0xd7, 0xf5, 0x33, 0xdf, 0xf3, 0xee,
+ 0x30, 0xfa, 0xef, 0x34, 0x35, 0x29, 0x17, 0xfc, 0x37, 0xdd, 0xfa, 0x04, 0xce, 0x56, 0x49, 0x01,
+ 0x7f, 0x78, 0x2f, 0x14, 0x2b, 0x2a, 0x01, 0x26, 0x2b, 0x3b, 0x01, 0x39, 0x11, 0x14, 0x06, 0x54,
+ 0x16, 0xbb, 0xd7, 0x38, 0xd4, 0xf8, 0xd9, 0xdf, 0xef, 0xd0, 0x11, 0x13, 0xc7, 0xf8, 0x01, 0xf9,
+ 0xf3, 0x13, 0x1c, 0xe5, 0xe5, 0xe9, 0x37, 0x2b, 0x01, 0x53, 0x55, 0x32, 0x07, 0xf9, 0xf1, 0x14,
+ 0x38, 0x04, 0xfc, 0x02, 0xe3, 0xfc, 0xf5, 0xf2, 0x04, 0x40, 0x0a, 0x13, 0x32, 0xee, 0xf1, 0x1d,
+ 0xa9, 0xfd, 0xf7, 0xc9, 0xe9, 0x03, 0xd2, 0xc4, 0xed, 0xf2, 0xeb, 0x47, 0xe6, 0x47, 0xee, 0x10,
+ 0xcf, 0xa5, 0x34, 0xf1, 0xf0, 0xea, 0xe9, 0xc6, 0xaa, 0x28, 0x9f, 0xff, 0x0a, 0xf3, 0xf1, 0xca,
+ 0xdd, 0x32, 0xe1, 0x35, 0xb3, 0x0f, 0xe5, 0xff, 0x46, 0x38, 0x1a, 0x46, 0x00, 0x3b, 0xb9, 0xd8,
+ 0xdc, 0x04, 0xff, 0x01, 0x26, 0x25, 0xe9, 0xaf, 0x28, 0x0a, 0xc5, 0xf5, 0x3e, 0x41, 0x2d, 0x46,
+ 0x2c, 0x67, 0x05, 0xda, 0x30, 0x14, 0x0f, 0x03, 0x05, 0x0f, 0x06, 0x1b, 0xe1, 0x8b, 0xdf, 0xa3,
+ 0xf7, 0xb9, 0xda, 0x0b, 0x30, 0x06, 0x3f, 0xf8, 0x6e, 0x7f, 0xfe, 0x1a, 0x2f, 0x18, 0xb0, 0xd2,
+ 0xa8, 0xc7, 0xc1, 0xe7, 0xdd, 0x06, 0x0c, 0x51, 0x2f, 0x2f, 0x2e, 0x08, 0xf3, 0x03, 0xc6, 0xc6,
+ 0xb6, 0xa3, 0x9c, 0xdc, 0x9e, 0xf2, 0x18, 0x11, 0x03, 0xf1, 0xb7, 0x31, 0xdc, 0xd4, 0x34, 0x96,
+ 0xab, 0xba, 0x9a, 0xde, 0xe4, 0xd0, 0xc7, 0x06, 0x40, 0xfe, 0x44, 0x29, 0xe1, 0x0a, 0xde, 0xce,
+ 0xeb, 0xbc, 0xe3, 0xc4, 0x00, 0xd1, 0xcd, 0x1d, 0xfe, 0x0b, 0x92, 0x02, 0xca, 0xe7, 0x0b, 0xc6,
+ 0xbe, 0xcb, 0xe6, 0x01, 0xde, 0x44, 0xf3, 0x0f, 0xfe, 0xf6, 0x02, 0x0c, 0xf9, 0x10, 0xe7, 0x19,
+ 0x0f, 0xd8, 0x3f, 0x18, 0x81, 0x42, 0x0f, 0x07, 0x0f, 0xf5, 0xfb, 0xea, 0xf7, 0x98, 0x22, 0xfb,
+ 0x32, 0xe0, 0xa4, 0x13, 0xcc, 0xff, 0x33, 0x0c, 0xd4, 0x47, 0xf0, 0xe5, 0xf0, 0x23, 0xe5, 0xda,
+ 0x1d, 0x40, 0xd0, 0x2c, 0xe5, 0x1e, 0x02, 0xfe, 0xb7, 0xf0, 0x1f, 0x1c, 0x1c, 0xfc, 0x1f, 0x2c,
+ 0x38, 0x47, 0x13, 0x2e, 0xe0, 0xc2, 0x37, 0x29, 0x1c, 0x1b, 0x81, 0xf5, 0x13, 0xca, 0x0b, 0x46,
+ 0x58, 0x24, 0x3a, 0xfd, 0x6b, 0xc9, 0xbc, 0x1d, 0x23, 0x08, 0x17, 0x48, 0x20, 0x5b, 0x56, 0xe7,
+ 0xc4, 0xf0, 0xdf, 0xe3, 0x1d, 0x1d, 0x50, 0x20, 0x51, 0xff, 0xf4, 0x44, 0x4d, 0x1a, 0x0b, 0x30,
+ 0x2d, 0x38, 0x46, 0x10, 0xcf, 0x01, 0x09, 0x15, 0x3d, 0x56, 0x28, 0xdb, 0xe8, 0xd9, 0xb7, 0xad,
+ 0x1a, 0xed, 0xce, 0xcf, 0xf3, 0x2b, 0x5a, 0x0b, 0xe6, 0x36, 0xc2, 0x9b, 0x51, 0x1e, 0x3e, 0xce,
+ 0xf5, 0x52, 0xd1, 0xca, 0xe7, 0xd0, 0xf0, 0xf4, 0x0c, 0xed, 0x21, 0x09, 0xdf, 0x91, 0xdb, 0xef,
+ 0x09, 0xf7, 0x14, 0x48, 0xd6, 0xe4, 0x2f, 0x05, 0xd8, 0x06, 0xbc, 0x81, 0xbf, 0x2a, 0xc7, 0xd8,
+ 0x07, 0xe9, 0x36, 0xe9, 0xcb, 0x08, 0x31, 0x0e, 0x01, 0x3e, 0x17, 0xd8, 0x16, 0xf3, 0xd3, 0xef,
+ 0xf8, 0x37, 0xdd, 0xdb, 0x49, 0x09, 0xe2, 0xe6, 0x02, 0xff, 0xeb, 0xd1, 0x0d, 0x90, 0xe7, 0x42,
+ 0x2d, 0xf4, 0xc1, 0xb7, 0x04, 0x05, 0x3f, 0x2e, 0x3a, 0x1d, 0xd9, 0x4a, 0x06, 0xff, 0xf9, 0xe4,
+ 0x0b, 0x41, 0x35, 0x37, 0x0a, 0x1b, 0x11, 0xca, 0xcb, 0xed, 0xfc, 0xe4, 0xcb, 0x24, 0xed, 0xcb,
+ 0x30, 0x02, 0xd3, 0x36, 0x2b, 0xe9, 0xee, 0x19, 0xb0, 0xf9, 0xaa, 0x17, 0x61, 0x0e, 0x25, 0x2f,
+ 0x2c, 0xf0, 0x13, 0xf7, 0x1f, 0xc8, 0xee, 0xf2, 0xd4, 0xb5, 0xde, 0x47, 0x43, 0x2b, 0x32, 0x03,
+ 0x39, 0xee, 0x44, 0x01, 0xd8, 0xb5, 0xf2, 0x23, 0x2b, 0xfc, 0x09, 0xb8, 0x1f, 0x3a, 0x2e, 0x38,
+ 0x0d, 0x36, 0x07, 0xe4, 0x30, 0x0b, 0xf3, 0x06, 0xab, 0x14, 0x45, 0xd3, 0xfb, 0x10, 0x4d, 0x26,
+ 0xc8, 0xff, 0xbb, 0xb9, 0x01, 0xe0, 0x2f, 0xd6, 0x37, 0xf9, 0x23, 0x13, 0x7f, 0xf6, 0xc3, 0xc7,
+ 0x00, 0x43, 0x5c, 0xd8, 0x02, 0x2a, 0x02, 0x2f, 0xf8, 0x0f, 0xf5, 0xf9, 0x2a, 0xe8, 0xa6, 0xe1,
+ 0xfc, 0xeb, 0xd8, 0xde, 0xf4, 0xdd, 0x23, 0x17, 0x06, 0x2d, 0x0f, 0xf2, 0x07, 0x44, 0xfb, 0xe0,
+ 0xc5, 0x33, 0x4a, 0x21, 0x22, 0xcc, 0xea, 0x2d, 0xa9, 0xbd, 0x2a, 0x50, 0x19, 0x19, 0xc6, 0xe2,
+ 0x41, 0xbd, 0x9b, 0xd3, 0x14, 0x3c, 0x27, 0x40, 0x09, 0xb6, 0xf2, 0x93, 0x2d, 0x1e, 0x02, 0x1b,
+ 0x0b, 0xce, 0x3b, 0x0d, 0x01, 0xc5, 0x02, 0x3f, 0xa1, 0xc9, 0xf4, 0x92, 0xd6, 0xe3, 0x48, 0x1c,
+ 0xfa, 0x41, 0xff, 0x0e, 0x12, 0xd7, 0xef, 0xf0, 0xe7, 0xc4, 0x08, 0xf3, 0x99, 0x2b, 0x04, 0xe4,
+ 0xce, 0x2f, 0xd2, 0xd5, 0xe3, 0xbb, 0xfd, 0xf2, 0x02, 0x18, 0x23, 0x55, 0xee, 0xe5, 0xdb, 0xee,
+ 0xd6, 0xc9, 0x1e, 0xea, 0xa6, 0xec, 0xe5, 0xf3, 0x0d, 0xf9, 0xd5, 0x04, 0x2a, 0xf9, 0xc7, 0x04,
+ 0xf6, 0x00, 0xd7, 0xec, 0xe8, 0xcf, 0xb2, 0xe5, 0x81, 0x04, 0x31, 0xfe, 0xe8, 0xda, 0xba, 0xb6,
+ 0x69, 0xd4, 0xdd, 0x46, 0x50, 0x2e, 0x23, 0x06, 0xed, 0x09, 0x07, 0x3e, 0x03, 0x1e, 0xfe, 0xea,
+ 0xb9, 0xfd, 0xf7, 0xd5, 0xd6, 0xf9, 0xe3, 0x22, 0xa4, 0xd9, 0xf9, 0xe6, 0xc2, 0x18, 0x3f, 0x1f,
+ 0x06, 0x11, 0xea, 0xc5, 0x05, 0xdc, 0x08, 0x2f, 0x17, 0x3f, 0x81, 0x8a, 0xac, 0xd7, 0xdb, 0xeb,
+ 0x22, 0x1f, 0xdd, 0xee, 0xff, 0xf1, 0xbd, 0xe6, 0xd1, 0x46, 0x13, 0xf4, 0x8a, 0x2f, 0x0c, 0xa7,
+ 0xfb, 0xb1, 0x57, 0xf7, 0xdc, 0xc4, 0xcd, 0x00, 0xd6, 0xdd, 0x15, 0x1b, 0x22, 0x03, 0x0d, 0x1a,
+ 0xfb, 0xfe, 0x4c, 0x25, 0x1a, 0xc0, 0xfa, 0xa7, 0xbc, 0x89, 0x5b, 0x05, 0xfb, 0x1f, 0x0c, 0x03,
+ 0x0f, 0xce, 0xc3, 0xb3, 0x19, 0x11, 0x07, 0x0d, 0xf6, 0x27, 0xfb, 0x08, 0x97, 0xff, 0x18, 0xc1,
+ 0x21, 0x14, 0x0d, 0xdc, 0x14, 0x96, 0xcc, 0x38, 0xca, 0xd5, 0xf4, 0x13, 0x1b, 0xec, 0xc2, 0x13,
+ 0xf7, 0xb5, 0xf9, 0x0b, 0x1b, 0xbf, 0xea, 0xe6, 0xe6, 0x3c, 0xc2, 0xf8, 0x5d, 0x2d, 0x0f, 0xe2,
+ 0x3f, 0xd0, 0xf1, 0xfb, 0xf6, 0x15, 0xbc, 0xe2, 0x35, 0xc5, 0x2a, 0x44, 0xc3, 0xfe, 0xff, 0xf9,
+ 0x07, 0xa9, 0x0a, 0xe7, 0xd2, 0xf1, 0xed, 0xdf, 0xef, 0x22, 0xf2, 0x4d, 0x43, 0xda, 0xad, 0x81,
+ 0xb8, 0xcd, 0x3a, 0x28, 0x05, 0xb0, 0x14, 0xda, 0xd2, 0xe8, 0x33, 0xdf, 0xfa, 0xfa, 0x2a, 0x40,
+ 0x0c, 0xd6, 0xce, 0xec, 0xff, 0x38, 0x1d, 0x54, 0x0b, 0x05, 0x05, 0xdb, 0x2d, 0xd5, 0x94, 0xf9,
+ 0xc4, 0xc9, 0x15, 0x26, 0x38, 0x5e, 0x0d, 0xa0, 0x08, 0xde, 0xe4, 0x15, 0x05, 0x17, 0xd1, 0x8b,
+ 0xd5, 0xf0, 0xfe, 0xfa, 0x2c, 0x0f, 0x24, 0x39, 0x29, 0xf8, 0x1d, 0xc4, 0xc0, 0xdd, 0xcc, 0xd4,
+ 0xf0, 0xf8, 0x1a, 0xd2, 0xed, 0xf3, 0xbb, 0xb7, 0xae, 0x16, 0xbd, 0xc1, 0xed, 0x11, 0x36, 0x2e,
+ 0x0c, 0x2d, 0xe0, 0xfd, 0xd9, 0x0b, 0x15, 0xf4, 0xb6, 0xc6, 0xca, 0xdf, 0x09, 0x46, 0xeb, 0x58,
+ 0x35, 0xd0, 0xf1, 0xdb, 0x13, 0xe8, 0xe0, 0xe4, 0xf6, 0x15, 0xde, 0x40, 0xab, 0x19, 0x0f, 0x17,
+ 0x08, 0xf4, 0x15, 0xcf, 0x2e, 0x44, 0xff, 0x04, 0x43, 0xed, 0x05, 0xca, 0x2c, 0x37, 0x1b, 0xd2,
+ 0x11, 0x0d, 0xd8, 0xde, 0xd6, 0x0d, 0xc9, 0x19, 0xbe, 0xec, 0xe4, 0xce, 0xcb, 0x14, 0x03, 0xc3,
+ 0xde, 0xef, 0x2a, 0x1e, 0x62, 0xf2, 0xd5, 0x3b, 0xda, 0x81, 0x32, 0x13, 0x42, 0xfb, 0xf3, 0xfb,
+ 0x08, 0xf5, 0xeb, 0x0a, 0x12, 0xdc, 0xf4, 0xf2, 0x02, 0x24, 0x55, 0x38, 0x13, 0x3d, 0x0c, 0x17,
+ 0xec, 0x0b, 0x7f, 0x1c, 0xe4, 0xcb, 0x5a, 0x24, 0xfd, 0x17, 0x1e, 0x04, 0x05, 0x0b, 0xf3, 0xde,
+ 0xc2, 0xfd, 0xdf, 0xff, 0x67, 0x27, 0xcd, 0xcc, 0xe2, 0xc2, 0xd3, 0xb5, 0x30, 0x2a, 0x20, 0x36,
+ 0xfa, 0x05, 0x37, 0xf2, 0x8b, 0xe3, 0x00, 0x11, 0xf7, 0x07, 0xb7, 0x97, 0x25, 0xf4, 0x08, 0x63,
+ 0xdd, 0xbc, 0xd0, 0xde, 0x1d, 0xf4, 0xdb, 0xfc, 0xdd, 0xe3, 0xed, 0x09, 0xed, 0x13, 0x05, 0xf7,
+ 0x1e, 0xe6, 0xeb, 0x05, 0xfd, 0xca, 0xfa, 0xf3, 0x12, 0xd2, 0xea, 0x10, 0x00, 0xe3, 0x1d, 0x47,
+ 0xa1, 0x46, 0x23, 0xda, 0x33, 0x24, 0xb8, 0x18, 0xf8, 0xaf, 0x06, 0x15, 0x1b, 0x1f, 0x22, 0xa2,
+ 0xc8, 0xd7, 0x0b, 0xfa, 0xe9, 0x04, 0xf8, 0x18, 0x9a, 0x6d, 0xfe, 0xff, 0x9c, 0x2e, 0x00, 0x00,
+ 0xa5, 0x70, 0xfe, 0xff, 0xe6, 0x0f, 0x00, 0x00, 0x15, 0x39, 0x01, 0x00, 0x21, 0x5f, 0xff, 0xff,
+ 0x49, 0x7e, 0xff, 0xff, 0x53, 0x77, 0x02, 0x00, 0x17, 0xb3, 0xff, 0xff, 0xaf, 0x86, 0xfd, 0xff,
+ 0x7b, 0xbf, 0x01, 0x00, 0x1c, 0xcf, 0xff, 0xff, 0x6c, 0x79, 0x00, 0x00, 0xc5, 0x59, 0xfe, 0xff,
+ 0xe7, 0xea, 0xfd, 0xff, 0xce, 0xd9, 0xfe, 0xff, 0x1e, 0x62, 0xff, 0xff, 0x51, 0x70, 0xff, 0xff,
0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x12, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x12, 0x00,
- 0x84, 0xc9, 0x4f, 0x3d, 0x80, 0xff, 0xff, 0xff, 0xea, 0x8a, 0xaa, 0x3c, 0x80, 0xff, 0xff, 0xff,
+ 0x77, 0x36, 0x2b, 0x3d, 0x80, 0xff, 0xff, 0xff, 0xe3, 0xde, 0xae, 0x3c, 0x80, 0xff, 0xff, 0xff,
0x02, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x08, 0x00, 0x00,
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x12, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00,
- 0xea, 0x8a, 0xaa, 0x3c, 0x80, 0xff, 0xff, 0xff, 0xcf, 0xb8, 0x18, 0x3e, 0x2f, 0x00, 0x00, 0x00,
+ 0xe3, 0xde, 0xae, 0x3c, 0x80, 0xff, 0xff, 0xff, 0xfa, 0x8f, 0xcf, 0x3d, 0x21, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x12, 0x29, 0x69, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0x22, 0x59, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x0c, 0x2b, 0x0e, 0x32, 0xbf, 0xa0, 0x0e,
- 0xab, 0x4a, 0xe8, 0xaf, 0xc9, 0xd7, 0xc1, 0x25, 0xa9, 0xda, 0xc4, 0x89, 0x9e, 0x50, 0xa5, 0xce,
- 0xe6, 0xa2, 0xbc, 0x1a, 0x5f, 0x01, 0xf5, 0x2f, 0x58, 0xc9, 0x0e, 0x00, 0x2d, 0x49, 0xa3, 0x41,
- 0x02, 0x35, 0xb0, 0xab, 0xdd, 0xf9, 0xaf, 0x34, 0x3d, 0x94, 0x04, 0x92, 0x0b, 0x5b, 0xad, 0xd9,
- 0x43, 0xe9, 0x1c, 0x43, 0xb3, 0x49, 0x19, 0x9a, 0x37, 0x39, 0x33, 0x84, 0xf4, 0xd1, 0xa2, 0xc4,
- 0x1d, 0xbe, 0x87, 0xa8, 0x8d, 0x07, 0x16, 0x26, 0xfc, 0x44, 0xb4, 0x33, 0xab, 0xe6, 0x0a, 0x3e,
- 0x70, 0xcb, 0xcc, 0xe7, 0x36, 0xa4, 0xed, 0xaf, 0xf1, 0xd8, 0x5f, 0xaf, 0xf7, 0xb0, 0x42, 0x3c,
- 0x39, 0x24, 0x81, 0x28, 0x97, 0x64, 0x0e, 0x04, 0x03, 0xe3, 0x92, 0xa8, 0x9b, 0x28, 0x85, 0x88,
- 0x02, 0x1a, 0x4e, 0x40, 0x16, 0xad, 0x08, 0x92, 0xcd, 0x51, 0x8d, 0xd6, 0x3f, 0x54, 0xe7, 0xcd,
- 0x23, 0x03, 0xf3, 0xb1, 0xb0, 0x07, 0xcb, 0x58, 0x0b, 0x39, 0xc0, 0xc1, 0x5f, 0x23, 0x48, 0xbe,
- 0xf5, 0xc3, 0x21, 0xb1, 0xc9, 0x2a, 0xd8, 0xa4, 0x19, 0xe9, 0xf9, 0x16, 0x44, 0xf2, 0xc0, 0x9a,
- 0x47, 0xee, 0x32, 0xa3, 0xc8, 0x68, 0x90, 0x2f, 0x99, 0xdd, 0x2b, 0xca, 0x00, 0x00, 0x00, 0x00,
- 0xeb, 0x74, 0xff, 0xff, 0xf7, 0x5d, 0xff, 0xff, 0xaa, 0xb7, 0xff, 0xff, 0xcd, 0x86, 0xff, 0xff,
- 0xf9, 0x8a, 0xff, 0xff, 0x25, 0x96, 0xff, 0xff, 0xc8, 0x34, 0xff, 0xff, 0xf7, 0x86, 0xff, 0xff,
- 0x31, 0xa2, 0xff, 0xff, 0x34, 0x85, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x27, 0xbd, 0x3a, 0x4d, 0x9b, 0xa4, 0x9d,
+ 0x02, 0xee, 0x2f, 0xe4, 0xc7, 0xef, 0xc2, 0x3b, 0x3b, 0xb3, 0xb8, 0xc1, 0xe9, 0x3d, 0x37, 0xd1,
+ 0xd8, 0x52, 0x30, 0x28, 0x02, 0xbc, 0xd2, 0xd1, 0xb0, 0xb1, 0xb9, 0x34, 0x3f, 0x48, 0x4e, 0xc6,
+ 0xe8, 0xc1, 0xd1, 0x3d, 0xab, 0xe2, 0xb0, 0x10, 0x41, 0xf9, 0xbf, 0xbe, 0x42, 0xd4, 0xc1, 0x2b,
+ 0x50, 0xef, 0xbe, 0x1c, 0xab, 0xcc, 0xb4, 0xf4, 0x06, 0xa9, 0xb2, 0x56, 0xf5, 0x26, 0x9f, 0x3b,
+ 0xcf, 0x3a, 0xc3, 0x38, 0x39, 0xfd, 0x47, 0xb1, 0xde, 0x0f, 0xd8, 0x43, 0xd4, 0xb6, 0x3b, 0x81,
+ 0xfc, 0x16, 0xe8, 0xbd, 0xb5, 0xdb, 0x0e, 0x5b, 0x11, 0xeb, 0x97, 0xba, 0x3a, 0xeb, 0x31, 0x2b,
+ 0xeb, 0x54, 0xa5, 0xc2, 0xeb, 0xb9, 0x9c, 0x52, 0xb0, 0x09, 0xce, 0x20, 0xb8, 0x43, 0xc0, 0xd4,
+ 0x5c, 0xa9, 0x2a, 0x1f, 0x0c, 0x90, 0x8f, 0x25, 0xe3, 0xa5, 0xcc, 0xbd, 0xfc, 0x2b, 0x38, 0xbe,
+ 0x3c, 0x41, 0xf2, 0x67, 0xe0, 0x99, 0x26, 0xe2, 0x45, 0xc8, 0x0b, 0xc6, 0xf5, 0x2c, 0x21, 0xb2,
+ 0x4f, 0x51, 0x9d, 0x14, 0xcf, 0x37, 0xaa, 0xf4, 0xdd, 0xa0, 0x39, 0xa5, 0xcc, 0xb7, 0x9a, 0xdd,
+ 0x53, 0xfc, 0x2a, 0xae, 0x15, 0x40, 0xb1, 0xf5, 0x45, 0x1a, 0xba, 0x32, 0x00, 0x00, 0x00, 0x00,
+ 0xd9, 0x88, 0xff, 0xff, 0x40, 0x6e, 0xff, 0xff, 0x6d, 0xb5, 0xff, 0xff, 0xef, 0x68, 0xff, 0xff,
+ 0x20, 0xc5, 0xff, 0xff, 0x14, 0x89, 0xff, 0xff, 0x12, 0x5b, 0xff, 0xff, 0x12, 0x9c, 0xff, 0xff,
+ 0x3b, 0xa3, 0xff, 0xff, 0x05, 0xa3, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x30, 0x00, 0x00, 0x00,
0x48, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00,
- 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, 0xcf, 0xb8, 0x18, 0x3e, 0x2f, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, 0xfa, 0x8f, 0xcf, 0x3d, 0x21, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x3b, 0x80, 0xff, 0xff, 0xff,
};
#endif
diff --git a/examples/mnist_cnn/mnist_cnn.h5 b/examples/mnist_cnn/mnist_cnn.h5
index 90f5af6..9aee050 100644
Binary files a/examples/mnist_cnn/mnist_cnn.h5 and b/examples/mnist_cnn/mnist_cnn.h5 differ
diff --git a/examples/mnist_cnn/mnist_cnn.tflite b/examples/mnist_cnn/mnist_cnn.tflite
index cb5b9d2..cbbc70e 100644
Binary files a/examples/mnist_cnn/mnist_cnn.tflite and b/examples/mnist_cnn/mnist_cnn.tflite differ
diff --git a/examples/mnist_cnn/mnist_cnn.tmdl b/examples/mnist_cnn/mnist_cnn.tmdl
index 9acbaa0..8a50b43 100644
Binary files a/examples/mnist_cnn/mnist_cnn.tmdl and b/examples/mnist_cnn/mnist_cnn.tmdl differ
diff --git a/examples/mnist_cnn/mnist_cnn_camera.py b/examples/mnist_cnn/mnist_cnn_camera.py
index e798fb9..e1ca01a 100644
--- a/examples/mnist_cnn/mnist_cnn_camera.py
+++ b/examples/mnist_cnn/mnist_cnn_camera.py
@@ -1,10 +1,12 @@
import time
+import array
# Using https://github.com/cnadler86/micropython-camera-API for ESP32 with OV2640 camera
from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling
+import downscale
def print_2d_buffer(arr, rowstride):
@@ -99,6 +101,9 @@ def main():
image_no = 0
+ process_size = 32
+ scaled = array.array('B', (0 for _ in range(process_size*process_size)))
+
while True:
capture_start = time.ticks_ms()
@@ -119,9 +124,16 @@ def main():
print('mean', mean)
path = f'img{image_no}.bmp'
- save_image(path, buf, width, height)
+ #save_image(path, buf, width, height)
#print_2d_buffer(buf, 96)
- print('Saved', path)
+ #print('Saved', path)
+
+ # Downscale
+ downscale_start = time.ticks_ms()
+ downscale.downscale(buf, scaled, width, process_size)
+ downscale_duration = time.ticks_diff(time.ticks_ms(), downscale_start)
+ print('downscale', downscale_duration)
+ # TODO: run CNN inference
image_no += 1
time.sleep(5.0)
diff --git a/examples/mnist_cnn/mnist_cnn_run.py b/examples/mnist_cnn/mnist_cnn_run.py
index b50d44e..92700e5 100644
--- a/examples/mnist_cnn/mnist_cnn_run.py
+++ b/examples/mnist_cnn/mnist_cnn_run.py
@@ -1,11 +1,23 @@
+import os
import array
-import emlearn_cnn
import time
import gc
+import emlearn_cnn
+
MODEL = 'mnist_cnn.tmdl'
-TEST_DATA_DIR = 'data/'
+TEST_DATA_DIR = 'test_data'
+
+def argmax(arr):
+ idx_max = 0
+ value_max = arr[0]
+ for i in range(1, len(arr)):
+ if arr[i] > value_max:
+ value_max = arr[i]
+ idx_max = i
+
+ return idx_max
def print_2d_buffer(arr, rowstride):
@@ -20,6 +32,27 @@ def print_2d_buffer(arr, rowstride):
gc.collect()
print('\n')
+def load_images_from_directory(path):
+ sep = '/'
+
+ for filename in os.listdir(path):
+ # TODO: support standard image formats, like .bmp/.png/.jpeg
+ if not filename.endswith('.bin'):
+ continue
+
+ # Find the label (if any). The last part, X_label.format
+ label = None
+ basename = filename.split('.')[0]
+ tok = basename.split('_')
+ if len(tok) > 2:
+ label = tok[-1]
+
+ data_path = path + sep + filename
+ with open(data_path, 'rb') as f:
+ img = array.array('B', f.read())
+
+ yield img, label
+
def test_cnn_mnist():
# load model
@@ -28,22 +61,32 @@ def test_cnn_mnist():
model_data = array.array('B', f.read())
model = emlearn_cnn.new(model_data)
+ out_length = model.output_dimensions()[0]
+ probabilities = array.array('f', (-1 for _ in range(out_length)))
+
# run on some test data
- for class_no in range(0, 10):
- data_path = TEST_DATA_DIR + 'mnist_example_{0:d}.bin'.format(class_no)
- #print('open', data_path)
- with open(data_path, 'rb') as f:
- img = array.array('B', f.read())
+ n_correct = 0
+ n_total = 0
+ for img, label in load_images_from_directory(TEST_DATA_DIR):
+ class_no = int(label) # mnist class labels are digits
- print_2d_buffer(img, 28)
+ #print_2d_buffer(img, 28)
- run_start = time.ticks_us()
- out = model.run(img)
- run_duration = time.ticks_diff(time.ticks_us(), run_start) / 1000.0 # ms
+ run_start = time.ticks_us()
+ model.run(img, probabilities)
+ out = argmax(probabilities)
+ run_duration = time.ticks_diff(time.ticks_us(), run_start) / 1000.0 # ms
+ correct = class_no == out
+ n_total += 1
+ if correct:
+ n_correct += 1
- print('mnist-example-check', class_no, out, class_no == out, run_duration)
+ print('mnist-example-check', class_no, '=', out, correct, round(run_duration, 3))
gc.collect()
+ accuracy = n_correct / n_total
+ print('mnist-example-done', n_correct, '/', n_total, round(accuracy*100, ), '%')
+
if __name__ == '__main__':
test_cnn_mnist()
diff --git a/examples/mnist_cnn/mnist_train.py b/examples/mnist_cnn/mnist_train.py
index 7c7a6e7..c1a7b74 100644
--- a/examples/mnist_cnn/mnist_train.py
+++ b/examples/mnist_cnn/mnist_train.py
@@ -40,7 +40,9 @@ def train_mnist(h5_file, epochs=10):
(x_orig_train, y_orig_train), (x_orig_test, y_orig_test) = mnist.load_data()
num_classes = 10
- generate_test_files('test_data', x_orig_test, y_orig_test)
+ TEST_DATA_DIR = 'test_data'
+ generate_test_files(TEST_DATA_DIR, x_orig_test, y_orig_test)
+ print('Wrote test data to', TEST_DATA_DIR)
x_train = x_orig_train
x_test = x_orig_test
@@ -58,7 +60,7 @@ def train_mnist(h5_file, epochs=10):
model.save(h5_file)
-def generate_test_files(out_dir, x, y):
+def generate_test_files(out_dir, x, y, samples_per_class=5):
if not os.path.exists(out_dir):
os.makedirs(out_dir)
@@ -71,15 +73,12 @@ def generate_test_files(out_dir, x, y):
# select one per class
for class_no in classes:
matches = (Y_classes == class_no)
- print('mm', matches.shape)
x_matches = X_series[matches]
- selected = x_matches.sample(n=1, random_state=1)
- for s in selected:
- print('ss', s.shape, s.dtype)
- print(s)
- out = os.path.join(out_dir, f'mnist_example_{class_no}.bin')
- data = s.tobytes(order='C')
+ selected = x_matches.sample(n=samples_per_class, random_state=1)
+ for i, sample in enumerate(selected):
+ out = os.path.join(out_dir, f'mnist_example_{i}_{class_no}.bin')
+ data = sample.tobytes(order='C')
assert len(data) == expect_bytes, (len(data), expect_bytes)
with open(out, 'wb') as f:
@@ -94,9 +93,10 @@ def generate_tinymaix_model(h5_file,
precision='fp32',
quantize_data=None,
quantize_type='0to1',
- output_dequantize=False,
):
+ output_dequantize = quantize_data is not None
+
# Convert .h5 to .tflite file
assert h5_file.endswith('.h5'), 'Keras model HDF5 file must end with .h5'
tflite_file = h5_file.replace('.h5', '.tflite')
diff --git a/examples/soundlevel_iir/README.md b/examples/soundlevel_iir/README.md
index f1ebabb..77ecec9 100644
--- a/examples/soundlevel_iir/README.md
+++ b/examples/soundlevel_iir/README.md
@@ -68,7 +68,7 @@ Please report back if you get this running on a device not listed here.
Make sure to have Python 3.10+ installed.
-Make sure to have the Unix port of MicroPython 1.23 setup.
+Make sure to have the Unix port of MicroPython setup.
On Windows you can use Windows Subsystem for Linux (WSL), or Docker.
Install the dependencies of this example:
diff --git a/examples/xor_trees/README.md b/examples/xor_trees/README.md
index a93a38d..5e8a2ab 100644
--- a/examples/xor_trees/README.md
+++ b/examples/xor_trees/README.md
@@ -11,7 +11,7 @@ Simple and an OK sanity check, but particularly useful.
Make sure to have Python 3.10+ installed.
-Make sure to have the Unix port of MicroPython 1.23 setup.
+Make sure to have the Unix port of MicroPython setup.
On Windows you can use Windows Subsystem for Linux (WSL), or Docker.
```console
diff --git a/src/emlearn_trees/trees.c b/src/emlearn_trees/trees.c
index 6c7460b..3404f60 100644
--- a/src/emlearn_trees/trees.c
+++ b/src/emlearn_trees/trees.c
@@ -197,8 +197,25 @@ static mp_obj_t builder_addleaf(mp_obj_t self_obj, mp_obj_t leaf_obj) {
static MP_DEFINE_CONST_FUN_OBJ_2(builder_addleaf_obj, builder_addleaf);
+// Return the shape of the output
+static mp_obj_t builder_get_outputs(mp_obj_t self_obj) {
+
+ mp_obj_trees_builder_t *o = MP_OBJ_TO_PTR(self_obj);
+ EmlTreesBuilder *self = &o->builder;
+
+ const int n_classes = self->trees.n_classes;
+ if (n_classes == 0) {
+ mp_raise_ValueError(MP_ERROR_TEXT("model not loaded"));
+ }
+
+ return mp_obj_new_int(n_classes);
+}
+static MP_DEFINE_CONST_FUN_OBJ_1(builder_get_outputs_obj, builder_get_outputs);
+
+
+
// Takes a array of input data
-static mp_obj_t builder_predict(mp_obj_t self_obj, mp_obj_t features_obj) {
+static mp_obj_t builder_predict(mp_obj_t self_obj, mp_obj_t features_obj, mp_obj_t output_obj) {
mp_obj_trees_builder_t *o = MP_OBJ_TO_PTR(self_obj);
EmlTreesBuilder *self = &o->builder;
@@ -212,6 +229,7 @@ static mp_obj_t builder_predict(mp_obj_t self_obj, mp_obj_t features_obj) {
const int16_t *features = bufinfo.buf;
const int n_features = bufinfo.len / sizeof(*features);
+ const int n_outputs = self->trees.n_classes;
#if EMLEARN_MICROPYTHON_DEBUG
mp_printf(&mp_plat_print,
@@ -222,18 +240,34 @@ static mp_obj_t builder_predict(mp_obj_t self_obj, mp_obj_t features_obj) {
);
#endif
+ if (n_features == 0 || n_outputs == 0) {
+ mp_raise_ValueError(MP_ERROR_TEXT("model not loaded"));
+ }
+
+ // Extract output
+ mp_get_buffer_raise(output_obj, &bufinfo, MP_BUFFER_RW);
+ if (bufinfo.typecode != 'f') {
+ mp_raise_ValueError(MP_ERROR_TEXT("expecting float output array"));
+ }
+ float *output_buffer = bufinfo.buf;
+ const int output_length = bufinfo.len / sizeof(*output_buffer);
+
+
// call model
- const int result = eml_trees_predict(&self->trees, features, n_features);
- if (result < 0) {
- mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("eml_trees_predict error"));
+ // NOTE: also handles checking of input and output lengths
+ const EmlError err = \
+ eml_trees_predict_proba(&self->trees, features, n_features, output_buffer, output_length);
+
+ if (err != EmlOk) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("eml_trees_predict_proba error"));
}
- return mp_obj_new_int(result);
+ return mp_const_none;
}
-static MP_DEFINE_CONST_FUN_OBJ_2(builder_predict_obj, builder_predict);
+static MP_DEFINE_CONST_FUN_OBJ_3(builder_predict_obj, builder_predict);
-mp_map_elem_t trees_locals_dict_table[6];
+mp_map_elem_t trees_locals_dict_table[7];
static MP_DEFINE_CONST_DICT(trees_locals_dict, trees_locals_dict_table);
// This is the entry point and is called when the module is imported
@@ -253,8 +287,9 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a
trees_locals_dict_table[3] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_addleaf), MP_OBJ_FROM_PTR(&builder_addleaf_obj) };
trees_locals_dict_table[4] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR___del__), MP_OBJ_FROM_PTR(&builder_del_obj) };
trees_locals_dict_table[5] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_setdata), MP_OBJ_FROM_PTR(&builder_setdata_obj) };
+ trees_locals_dict_table[6] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_outputs), MP_OBJ_FROM_PTR(&builder_get_outputs_obj) };
- MP_OBJ_TYPE_SET_SLOT(&trees_builder_type, locals_dict, (void*)&trees_locals_dict, 6);
+ MP_OBJ_TYPE_SET_SLOT(&trees_builder_type, locals_dict, (void*)&trees_locals_dict, 7);
// This must be last, it restores the globals dict
MP_DYNRUNTIME_INIT_EXIT
diff --git a/src/tinymaix_cnn/mod_cnn.c b/src/tinymaix_cnn/mod_cnn.c
index 675baa1..b6312fd 100644
--- a/src/tinymaix_cnn/mod_cnn.c
+++ b/src/tinymaix_cnn/mod_cnn.c
@@ -7,11 +7,14 @@
#include
#include "tm_layers.c"
+//#include "tm_layers_O1.c"
#include "tm_model.c"
//#include "tm_stat.c"
#include
+#define DEBUG (1)
+
// memset is used by some standard C constructs
#if !defined(__linux__)
@@ -34,42 +37,37 @@ void NORETURN abort() {
}
#endif
-
-static tm_err_t layer_cb(tm_mdl_t* mdl, tml_head_t* lh)
-{
-#if 0
- //dump middle result
- int h = lh->out_dims[1];
- int w = lh->out_dims[2];
- int ch= lh->out_dims[3];
- mtype_t* output = TML_GET_OUTPUT(mdl, lh);
- return TM_OK;
- TM_PRINTF("Layer %d callback ========\n", mdl->layer_i);
- #if 1
- for(int y=0; ylayer_body = mdl->b->layers_body;
+ for(mdl->layer_i = 0; mdl->layer_i < mdl->b->layer_cnt; mdl->layer_i++){
+ tml_head_t* h = (tml_head_t*)(mdl->layer_body);
+ if(h->is_out) {
+ if (out_idx < out_length) {
+ memcpy((void*)(&out[out_idx]), (void*)(&(h->out_dims)), sizeof(uint16_t)*4);
+ out_idx += 1;
+ } else {
+ return -1;
}
- TM_PRINTF("],");
}
- TM_PRINTF("],\n");
+ mdl->layer_body += (h->size);
}
- TM_PRINTF("\n");
- #endif
- return TM_OK;
-#else
- return TM_OK;
+ return out_idx;
+}
+
+static tm_err_t layer_cb(tm_mdl_t* mdl, tml_head_t* lh)
+{
+#if DEBUG
+ mp_printf(&mp_plat_print, "cnn-layer-cb type=%d \n", lh->type);
#endif
+
+ return TM_OK;
}
-#define DEBUG (1)
// MicroPython type
typedef struct _mp_obj_mod_cnn_t {
@@ -79,6 +77,7 @@ typedef struct _mp_obj_mod_cnn_t {
tm_mat_t input;
uint8_t *model_buffer;
uint8_t *data_buffer;
+ uint16_t out_dims[4];
} mp_obj_mod_cnn_t;
mp_obj_full_type_t mod_cnn_type;
@@ -118,8 +117,28 @@ static mp_obj_t mod_cnn_new(mp_obj_t model_data_obj) {
// will set the dimensions of the input matrix
tm_err_t load_err = tm_load(model, o->model_buffer, o->data_buffer, layer_cb, &o->input);
if (load_err != TM_OK) {
- mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("eml_fft_forward error"));
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("tm_load error"));
+ }
+
+ // find model output shape
+ o->out_dims[0] = 0;
+ tm_mat_t outs[1];
+ const int outputs = tm_get_outputs(model, outs, 1);
+ if (outputs != 1) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("only 1 output supported"));
}
+ memcpy((void*)(o->out_dims), (void*)(&(outs[0])), sizeof(uint16_t)*4);
+
+ if ((o->out_dims[0] != 1)) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("output must be 1d"));
+ }
+ memcpy((void*)(o->out_dims), (void*)(&(outs[0])), sizeof(uint16_t)*4);
+
+#if DEBUG
+ mp_printf(&mp_plat_print, "cnn-new-done in.dims=(%d,%d,%d,%d) out.dims=(%d,%d,%d,%d) \n",
+ o->input.dims, o->input.h, o->input.w, o->input.c,
+ o->out_dims[0], o->out_dims[1], o->out_dims[2], o->out_dims[3]);
+#endif
return MP_OBJ_FROM_PTR(o);
}
@@ -141,7 +160,7 @@ static MP_DEFINE_CONST_FUN_OBJ_1(mod_cnn_del_obj, mod_cnn_del);
// Add a node to the tree
-static mp_obj_t mod_cnn_run(mp_obj_t self_obj, mp_obj_t input_obj) {
+static mp_obj_t mod_cnn_run(mp_obj_t self_obj, mp_obj_t input_obj, mp_obj_t output_obj) {
mp_obj_mod_cnn_t *o = MP_OBJ_TO_PTR(self_obj);
@@ -149,7 +168,7 @@ static mp_obj_t mod_cnn_run(mp_obj_t self_obj, mp_obj_t input_obj) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(input_obj, &bufinfo, MP_BUFFER_RW);
if (bufinfo.typecode != 'B') {
- mp_raise_ValueError(MP_ERROR_TEXT("expecting float array"));
+ mp_raise_ValueError(MP_ERROR_TEXT("expecting byte array"));
}
uint8_t *input_buffer = bufinfo.buf;
const int input_length = bufinfo.len / sizeof(*input_buffer);
@@ -160,6 +179,21 @@ static mp_obj_t mod_cnn_run(mp_obj_t self_obj, mp_obj_t input_obj) {
mp_raise_ValueError(MP_ERROR_TEXT("wrong input size"));
}
+ // Extract output
+ mp_get_buffer_raise(output_obj, &bufinfo, MP_BUFFER_RW);
+ if (bufinfo.typecode != 'f') {
+ mp_raise_ValueError(MP_ERROR_TEXT("expecting float array"));
+ }
+ float *output_buffer = bufinfo.buf;
+ const int output_length = bufinfo.len / sizeof(*output_buffer);
+
+
+ // check buffer size wrt input
+ const int expect_out_length = o->out_dims[1]*o->out_dims[2]*o->out_dims[3];
+ if (output_length != expect_out_length) {
+ mp_raise_ValueError(MP_ERROR_TEXT("wrong output size"));
+ }
+
// Preprocess data
tm_mat_t in_uint8 = o->input;
in_uint8.data = (mtype_t*)input_buffer;
@@ -181,27 +215,48 @@ static mp_obj_t mod_cnn_run(mp_obj_t self_obj, mp_obj_t input_obj) {
mp_raise_ValueError(MP_ERROR_TEXT("run error"));
}
+ // Copy output
tm_mat_t out = outs[0];
- float* data = out.dataf;
- float maxp = 0;
- int maxi = -1;
-
- // TODO: pass the entire output vector out to Python
- // FIXME: unhardcode output handling
- for(int i=0; i<10; i++){
- //printf("%d: %.3f\n", i, data[i]);
- if (data[i] > maxp) {
- maxi = i;
- maxp = data[i];
- }
+
+#if DEBUG
+ mp_printf(&mp_plat_print, "cnn-run out.dims=(%d,%d,%d,%d) expect_length=%d \n",
+ out.dims, out.h, out.w, out.c, expect_out_length
+ );
+#endif
+
+ if (!((out.dims == 1) && (out.h == 1) && (out.w == 1) && out.c == expect_out_length)) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("unexpected output dims"));
+ }
+
+ for(int i=0; iout_dims[0];
+ mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(dimensions, NULL));
+
+ // A regular output should have C channels, and 1 for everything else
+ // TODO: support other shapes?
+ //dims==1, 11c
+ if (!(o->out_dims[0] == 1 && o->out_dims[1] == 1 && o->out_dims[2] == 1)) {
+ mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("wrong output shape"));
}
- return mp_obj_new_int(maxi);
+ tuple->items[0] = mp_obj_new_int(o->out_dims[3]);
+ return tuple;
}
-static MP_DEFINE_CONST_FUN_OBJ_2(mod_cnn_run_obj, mod_cnn_run);
+static MP_DEFINE_CONST_FUN_OBJ_1(mod_cnn_output_dimensions_obj, mod_cnn_output_dimensions);
-mp_map_elem_t mod_locals_dict_table[2];
+mp_map_elem_t mod_locals_dict_table[3];
static MP_DEFINE_CONST_DICT(mod_locals_dict, mod_locals_dict_table);
// This is the entry point and is called when the module is imported
@@ -217,6 +272,7 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a
// methods
mod_locals_dict_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_run), MP_OBJ_FROM_PTR(&mod_cnn_run_obj) };
mod_locals_dict_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR___del__), MP_OBJ_FROM_PTR(&mod_cnn_del_obj) };
+ mod_locals_dict_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_output_dimensions), MP_OBJ_FROM_PTR(&mod_cnn_output_dimensions_obj) };
MP_OBJ_TYPE_SET_SLOT(&mod_cnn_type, locals_dict, (void*)&mod_locals_dict, 2);
diff --git a/tests/test_cnn.py b/tests/test_cnn.py
index 1bf439e..b1b3aa0 100644
--- a/tests/test_cnn.py
+++ b/tests/test_cnn.py
@@ -12,6 +12,9 @@ def test_cnn_create():
model_data = array.array('B', f.read())
model = emlearn_cnn.new(model_data)
+ out_shape = model.output_dimensions()
+ assert out_shape == (10,), (out_shape)
+
# TODO: enable these checks
#wrong_type = array.array('f', [])
#model.run(wrong_type)
@@ -35,6 +38,16 @@ def print_2d_buffer(arr, rowstride):
print('\n')
+def argmax(arr):
+ idx_max = 0
+ value_max = arr[0]
+ for i in range(1, len(arr)):
+ if arr[i] > value_max:
+ value_max = arr[i]
+ idx_max = i
+
+ return idx_max
+
def test_cnn_mnist():
model = None
@@ -42,6 +55,8 @@ def test_cnn_mnist():
model_data = array.array('B', f.read())
model = emlearn_cnn.new(model_data)
+ probabilities = array.array('f', (-1 for _ in range(10)))
+
correct = 0
for class_no in range(0, 10):
data_path = MNIST_DATA_DIR + 'mnist_example_{0:d}.bin'.format(class_no)
@@ -51,13 +66,14 @@ def test_cnn_mnist():
#print_2d_buffer(img, 28)
- out = model.run(img)
+ model.run(img, probabilities)
+ out = argmax(probabilities)
# TODO replace with assert
print('mnist-example-check', class_no, out, class_no == out)
if out == class_no:
correct += 1
- assert correct >= 6, correct
+ assert correct >= 9, correct
test_cnn_create()
diff --git a/tests/test_trees.py b/tests/test_trees.py
index cca418a..a968f64 100644
--- a/tests/test_trees.py
+++ b/tests/test_trees.py
@@ -4,6 +4,16 @@
import array
import gc
+def argmax(arr):
+ idx_max = 0
+ value_max = arr[0]
+ for i in range(1, len(arr)):
+ if arr[i] > value_max:
+ value_max = arr[i]
+ idx_max = i
+
+ return idx_max
+
def test_trees_del():
"""
Deleting the model should free all the memory
@@ -45,9 +55,12 @@ def test_trees_xor():
( [1*s, 0], 1 ),
]
+ out = array.array('f', range(model.outputs()))
+
for (ex, expect) in examples:
f = array.array('h', ex)
- result = model.predict(f)
+ model.predict(f, out)
+ result = argmax(out)
assert result == expect, (ex, expect, result)
test_trees_del()