← Back to ratslair.com
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdvin <[email protected]>2025-12-22 11:14:02 +0100
committerEdvin <[email protected]>2025-12-22 11:14:02 +0100
commit93a45dacb2a2f7179baa0fdf89d6390518abb81a (patch)
tree081687018f93cfad16b52e2333dadc0934b377ac
parentd64ce2dc70a07285bb3222e69463e11385fc9e27 (diff)
Final commit
-rw-r--r--README.md3
m---------components/EZWIFI0
-rw-r--r--get_data.py60
-rw-r--r--gui.py154
-rw-r--r--main/CDIO.c328
-rw-r--r--train.py270
6 files changed, 632 insertions, 183 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6ad323d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# Cloning this repository
+
+Use `--recurse-submodules` to get the components when pulling the repo.
diff --git a/components/EZWIFI b/components/EZWIFI
-Subproject 6b5b31308f473b0c6eac340ab4ca55bc7358359
+Subproject 85220756de6e04b68023e31525c538aaeeb57e3
diff --git a/get_data.py b/get_data.py
index fc36121..dffee35 100644
--- a/get_data.py
+++ b/get_data.py
@@ -1,10 +1,10 @@
# This Python script is used to communicate with an ESP32 network and receive CSI data
# SET THESE VARIABLES
-name = "test_v2" # between ESPs (m)
-category = "p" # presence or no presence or activity {"p", "n", "a"}
+name = "alone" # between ESPs (m)
+category = "n" # presence or no presence or activity {"p", "n", "a"}
-path = f"{name}/{category}/"
+path = f"../datasets/{name}/{category}/"
import os
@@ -17,6 +17,11 @@ import numpy as np
import matplotlib.pyplot as plt
import collections
import datetime
+import sys, select, tty, termios
+
+# Is there data on stdin?
+def isData():
+ return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])
amplitude = collections.deque(maxlen=50)
phase = collections.deque(maxlen=50)
@@ -39,44 +44,40 @@ fig.add_axes(ax)
fig.canvas.draw()
plt.show(block=False)
-i = 0
img_i = 0
+i = 0
while 1:
data = esp_serial.readline().decode(errors='ignore')
- if 'CSI DATA' in data:
- data = re.findall(r"\(.*?\)", data)
+ if 'CSI_DATA' in data:
+ data = re.findall(r"\[(.*?)\]", data)
+
+ data = data[0].split()
csi_size = len(data)
# print(csi_size)
- if csi_size == 192:
+ if csi_size == 384:
amplitudes = []
phases = []
- iteration = 0
- for tup in data:
- tup = re.sub(r'[()\ ]', '', tup)
- ints = tup.split(",")
- a = 0
- b = 0
-
- if ints[0].isdigit() or (ints[0].startswith('-') and ints[0][1:].isdigit()):
- a = int(ints[0])
- if ints[1].isdigit() or (ints[1].startswith('-') and ints[1][1:].isdigit()):
- b = int(ints[1])
-
- # (iteration > 5 and iteration < 32) or (iteration > 32 and iteration < 59)
- # or (iteration > 65 and iteration < 123) or (iteration > 133 and iteration < 191):
- if (iteration > 65 and iteration < 123):
- # Non-logarithmic
- amplitudes.append(np.sqrt(a ** 2 + b ** 2))
- phases.append(np.atan2(b, a))
+ real = []
+ imag = []
+
+ buf_i = i
+ for i in range(int(csi_size/2)):
+ real.append(int(data[i * 2]))
+ imag.append(int(data[(i * 2) + 1]))
- iteration += 1
+ #if (i > 65 and i < 123):
+ # Non-logarithmic
+ amplitudes.append(np.sqrt(real[i] ** 2 + imag[i] ** 2))
+ phases.append(np.atan2(imag[i], real[i]))
+ i = buf_i
+
amplitude.append(amplitudes)
phase.append(phases)
@@ -84,7 +85,8 @@ while 1:
# df has shape (50, 58) -> (samples, freqs)
df = np.clip(np.asarray(amplitude, dtype=np.float32) * (255/35), 0, 255) # Get max 255, min 0
- plt.pcolormesh(np.transpose(df), cmap='gray')
+ plt.pcolormesh(np.transpose(df), cmap='gray', vmin=0, vmax=255)
+ plt.title(f"Gathering data ({category})\nImage #{img_i}", fontsize=30)
plt.axis('off')
fig.canvas.flush_events()
@@ -103,8 +105,8 @@ while 1:
plt.imsave(f"{path}{date}.png", img, cmap='gray')
print(f"Image: {img_i}")
- if img_i == 55:
- exit()
+ # if img_i == 55:
+ # exit()
i = 0
img_i += 1
diff --git a/gui.py b/gui.py
index d7641af..9a33cb5 100644
--- a/gui.py
+++ b/gui.py
@@ -1,18 +1,10 @@
# This Python script is used to communicate with an ESP32 network and receive CSI data
# SET THESE VARIABLES
-# name = "test" # between ESPs (m)
-# category = "a" # presence or no presence {"p", "n"}
-#
-# path = f"{name}/{category}/"
-#
-import os
-# Create base and label subfolders
-# true_path = os.path.join(base_path, "True")
-# false_path = os.path.join(base_path, "False")
+model_name = "alone"
-# os.makedirs(path, exist_ok=True)
+import os
import serial, re
import numpy as np
@@ -49,95 +41,99 @@ from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing import image
import tensorflow as tf
-model = load_model('model.h5')
+model = load_model(f"{model_name}.keras")
model.summary()
-#class_names = ["Activity", "No presence", "Presence"]
+activity_model = load_model(f"{model_name}_activities.keras")
+
+class_names = ["No presence", "Presence"]
-class_names = ["n", "p"]
+#class_names_activity = ["Run", "Sit", "Stand", "Walk"]
+class_names_activity = ["Sit", "Stand", "Walk"]
+
+confidence = 0
while 1:
data = esp_serial.readline().decode(errors='ignore')
+ # print(data)
- if 'CSI DATA' in data:
- data = re.findall(r"\(.*?\)", data)
+ if 'CSI_DATA' in data:
+ data = re.findall(r"\[(.*?)\]", data)
- csi_size = len(data)
+ print(len(data))
+ print(type(data))
- # print(csi_size)
+ if len(data) > 0:
+ data = data[0].split()
- if csi_size == 192:
- amplitudes = []
- phases = []
+ csi_size = len(data)
- iteration = 0
- for tup in data:
- tup = re.sub(r'[()\ ]', '', tup)
- ints = tup.split(",")
- a = 0
- b = 0
+ # print(csi_size)
- if ints[0].isdigit() or (ints[0].startswith('-') and ints[0][1:].isdigit()):
- a = int(ints[0])
- if ints[1].isdigit() or (ints[1].startswith('-') and ints[1][1:].isdigit()):
- b = int(ints[1])
+ if csi_size == 384:
+ amplitudes = []
+ phases = []
- # (iteration > 5 and iteration < 32) or (iteration > 32 and iteration < 59)
- # or (iteration > 65 and iteration < 123) or (iteration > 133 and iteration < 191):
- if (iteration > 65 and iteration < 123):
- # Non-logarithmic
- amplitudes.append(np.sqrt(a ** 2 + b ** 2))
- phases.append(np.atan2(b, a))
+ real = []
+ imag = []
+
+ buf_i = i
+ for i in range(int(csi_size/2)):
+ real.append(int(data[i * 2]))
+ imag.append(int(data[(i * 2) + 1]))
- iteration += 1
+ #if (i > 65 and i < 123):
+ # Non-logarithmic
+ amplitudes.append(np.sqrt(real[i] ** 2 + imag[i] ** 2))
+ phases.append(np.atan2(imag[i], real[i]))
- amplitude.append(amplitudes)
- phase.append(phases)
+ i = buf_i
+
+ amplitude.append(amplitudes)
+ phase.append(phases)
- plt.clf()
+ plt.clf()
- # df has shape (50, 58) -> (samples, freqs)
- df = np.clip(np.asarray(amplitude, dtype=np.float32) * (255/35), 0, 255) # Get max 255, min 0
- plt.pcolormesh(np.transpose(df), cmap='gray')
- plt.axis('off')
+ # df has shape (50, 58) -> (samples, frequencies)
+ df = np.clip(np.asarray(amplitude, dtype=np.float32) * (255/35), 0, 255) # Get max 255, min 0
+ plt.pcolormesh(np.transpose(df), cmap='gray', vmin=0, vmax=255)
+ plt.axis('off')
- fig.canvas.flush_events()
- plt.show()
+ i += 1
- date = datetime.datetime.now().strftime("%Y-%m-%d %H%M%S")
- i += 1
- if img_i > 1:
- # img = image.load_img(
- # "pred.png",
- # target_size=(57, 50),
- # color_mode="grayscale"
- # )
- # img_array = image.img_to_array(img)
- # img_array = tf.expand_dims(img_array, 0)
- df = tf.expand_dims(np.transpose(df), 0)
- prediction = model.predict(np.array(df))
- prediction = prediction.argmax(axis=-1)[0]
- print(f"{class_names[prediction - 1]}")
-
- if class_names[prediction - 1] == "n":
- esp_serial.write(b"red")
- else:
- esp_serial.write(b"green")
-
- if i == 50:
- if img_i > 0:
- df = np.clip(np.asarray(amplitude, dtype=np.float32) * (255/35), 0, 255) # Get max 255, min 0
- # Save a 58X50 pixel image (freq x samples), matching the live plot data
- img = np.transpose(df) # shape (58, 50) -> 58 px high, 50 px wide
- # img_norm = np.clip(img / 35.0, 0, 1) # normalize like vmin=0, vmax=35
+ if img_i > 1:
+ df = tf.expand_dims(np.transpose(df), 0)
+ prediction = model.predict(np.array(df), verbose=0)
+ prediction = prediction.argmax(axis=-1)[0]
+ # print(f"{prediction}")
+
+ if class_names[prediction] == "Presence":
+ if confidence < 10:
+ confidence += 1
+ else:
+ if confidence > -10:
+ confidence -= 1
+
+ # print(f"Confidence: {confidence}")
+ if confidence <= 0:
+ prediction = 0
+ esp_serial.write(b"green")
+ plt.title(f"{class_names[prediction]}", fontsize=50)
+ else:
+ prediction = 1
+ activity_prediction = activity_model.predict(np.array(df), verbose=0)
+ activity_prediction = activity_prediction.argmax(axis=-1)[0]
+ esp_serial.write(b"red")
+
+ plt.title(f"{class_names[prediction]}: {class_names_activity[activity_prediction]}", fontsize=50)
- plt.imsave("pred.png", img, cmap='gray')
-
- print(f"Image: {img_i}")
- if img_i == 55:
- exit()
- i = 0
- img_i += 1
+
+
+ if i == 50:
+ i = 0
+ img_i += 1
+ fig.canvas.flush_events()
+ plt.show()
diff --git a/main/CDIO.c b/main/CDIO.c
index bc77561..8e76b64 100644
--- a/main/CDIO.c
+++ b/main/CDIO.c
@@ -4,74 +4,29 @@
#include "driver/uart.h"
#include "EZADC.h"
-
-#include "rgb_led.h"
-
#include "EZWIFI.h"
+#include "rgb_led.h"
-rgb_led MY_LED;
-
-char *data = (char *) "1\n";
-
-void socket_transmitter_sta_loop(bool (*is_wifi_connected)()) {
- int socket_fd = -1;
- while (1) {
- close(socket_fd);
- char *ip = (char *) "192.168.4.1";
- struct sockaddr_in caddr;
- caddr.sin_family = AF_INET;
- caddr.sin_port = htons(2223);
- while (!is_wifi_connected()) {
- // wait until connected to AP
- printf("wifi not connected. waiting...\n");
- vTaskDelay(1000 / portTICK_PERIOD_MS);
- }
- printf("initial wifi connection established.\n");
- if (inet_aton(ip, &caddr.sin_addr) == 0) {
- printf("ERROR: inet_aton\n");
- continue;
- }
+#define AP 1
- socket_fd = socket(PF_INET, SOCK_DGRAM, 0);
- if (socket_fd == -1) {
- printf("ERROR: Socket creation error [%s]\n", strerror(errno));
- continue;
- }
- if (connect(socket_fd, (const struct sockaddr *) &caddr, sizeof(struct sockaddr)) == -1) {
- printf("ERROR: socket connection error [%s]\n", strerror(errno));
- continue;
- }
+#define SSID "CDIO"
- printf("sending frames.\n");
- int i = 0;
- while (1) {
- //double start_time = get_steady_clock_timestamp();
- if (!is_wifi_connected()) {
- printf("ERROR: wifi is not connected\n");
- break;
- }
+#define CONFIG_WIFI_BANDWIDTH WIFI_BW_HT40
+#define CONFIG_SEND_FREQUENCY 20
+#define CONFIG_LESS_INTERFERENCE_CHANNEL 11
- if (sendto(socket_fd, &data, strlen(data), 0, (const struct sockaddr *) &caddr, sizeof(caddr)) !=
- strlen(data)) {
- vTaskDelay(1);
- continue;
- }
+#define PORT 3333
+#define KEEPALIVE_IDLE 1
+#define KEEPALIVE_INTERVAL 1
+#define KEEPALIVE_COUNT 1
- vTaskDelay(pdMS_TO_TICKS(100));
+static const char *TAG = "CDIO CSI";
- //double end_time = get_steady_clock_timestamp();
- //lag = end_time - start_time;
- }
- }
-}
+static const char *payload = "ESP";
-/* TaskHandle_t xHandle = NULL; */
+static int port_iterate;
-/* void vTask_socket_transmitter_sta_loop(void *pvParamteres) { */
-/* for(;;) { */
-/* socket_transmitter_sta_loop(&is_wifi_connected); */
-/* } */
-/* } */
+rgb_led MY_LED;
static void echo_task(void *arg)
{
@@ -123,31 +78,254 @@ static void echo_task(void *arg)
}
}
+void battery_task (void *pvParameters)
+{
+ int battery_voltage;
+ ADC MY_ADC;
+ init_adc(&MY_ADC);
-void app_main(void)
+ config_adc(&MY_ADC, 7);
+
+ while(1)
+ {
+ battery_voltage = ez_read(&MY_ADC);
+ //ESP_LOGI(TAG, "Battery voltage: %d", battery_voltage);
+
+ if(battery_voltage > 2000) {
+ rgb_set_color(&MY_LED, rgb_green);
+ }
+ else if(battery_voltage > 1800) {
+ rgb_set_color(&MY_LED, rgb_yellow);
+ }
+ else {
+ rgb_set_color(&MY_LED, rgb_red);
+ }
+
+ vTaskDelay(pdMS_TO_TICKS(1000));
+ }
+}
+
+static void do_retransmit(const int sock)
+{
+ int len;
+ char rx_buffer[128];
+ int8_t csi_buffer[256];
+ wifi_csi_info_t *info;
+
+ do {
+ len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
+ if (len < 0) {
+ ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
+ } else if (len == 0) {
+ ESP_LOGW(TAG, "Connection closed");
+ } else {
+ rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
+ ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);
+
+ if (strcmp(rx_buffer, "CSI") == 0)
+ {
+ info = get_csi();
+
+ wifi_csi_info_t d = info[0];
+ char mac[20] = {0};
+ sprintf(mac,"%02X:%02X:%02X:%02X:%02X:%02X", d.mac[0], d.mac[1], d.mac[2], d.mac[3], d.mac[4], d.mac[5]);
+
+ ets_printf("MAC: %s\nLength: %d\n", mac, info->len);
+
+ send(sock, info->buf, sizeof(info->buf), 0);
+ }
+ // else
+ // {
+ // send() can return less bytes than supplied length.
+ // Walk-around for robust implementation.
+ int to_write = len;
+ while (to_write > 0) {
+ int written = send(sock, rx_buffer + (len - to_write), to_write, 0);
+ if (written < 0) {
+ ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
+ // Failed to retransmit, giving up
+ return;
+ }
+ to_write -= written;
+ // }
+ }
+ }
+ } while (len > 0);
+}
+
+static void tcp_server_task(void *pvParameters)
+{
+ char addr_str[128];
+ int addr_family = (int)pvParameters;
+ int ip_protocol = 0;
+ int keepAlive = 1;
+ int keepIdle = KEEPALIVE_IDLE;
+ int keepInterval = KEEPALIVE_INTERVAL;
+ int keepCount = KEEPALIVE_COUNT;
+ struct sockaddr_storage dest_addr;
+
+ if (addr_family == AF_INET) {
+ struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
+ dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
+ dest_addr_ip4->sin_family = AF_INET;
+ dest_addr_ip4->sin_port = htons(PORT + port_iterate);
+ ip_protocol = IPPROTO_IP;
+ }
+
+ int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
+ if (listen_sock < 0) {
+ ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
+ vTaskDelete(NULL);
+ return;
+ }
+ int opt = 1;
+ setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+ ESP_LOGI(TAG, "Socket created");
+
+ int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
+ if (err != 0) {
+ ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
+ ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
+ goto CLEAN_UP;
+ }
+ ESP_LOGI(TAG, "Socket bound, port %d", PORT);
+
+ err = listen(listen_sock, 1);
+ if (err != 0) {
+ ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
+ goto CLEAN_UP;
+ }
+
+ while (1) {
+
+ ESP_LOGI(TAG, "Socket listening");
+
+ struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
+ socklen_t addr_len = sizeof(source_addr);
+ int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
+ if (sock < 0) {
+ ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
+ break;
+ }
+
+ // Set tcp keepalive option
+ setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
+ setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
+ setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
+ setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
+ // Convert ip address to string
+ if (source_addr.ss_family == PF_INET) {
+ inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
+ }
+
+ ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
+
+ do_retransmit(sock);
+
+ shutdown(sock, 0);
+ close(sock);
+ }
+
+CLEAN_UP:
+ close(listen_sock);
+ vTaskDelete(NULL);
+}
+
+void tcp_client_task(void *pvParameters)
{
+ char rx_buffer[128];
+ char host_ip[] = "192.168.4.1";
+ int addr_family = 0;
+ int ip_protocol = 0;
+
+ while(1)
+ {
+ struct sockaddr_in dest_addr;
+ inet_pton(AF_INET, host_ip, &dest_addr.sin_addr);
+ dest_addr.sin_family = AF_INET;
+ dest_addr.sin_port = htons(PORT);
+ addr_family = AF_INET;
+ ip_protocol = IPPROTO_IP;
+
+ int connected = 0;
+ while(!connected)
+ {
+ connected = is_wifi_connected();
+ vTaskDelay(pdTICKS_TO_MS(1000));
+ }
+
+ int sock = socket(addr_family, SOCK_STREAM, ip_protocol);
+ if (sock < 0) {
+ ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
+ break;
+ }
+ ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);
+
+ int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
+ if (err != 0) {
+ ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
+ break;
+ }
+ ESP_LOGI(TAG, "Successfully connected");
+
+ while (1) {
+ int err = send(sock, payload, strlen(payload), 0);
+ if (err < 0) {
+ ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
+ break;
+ }
+
+ int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
+ // Error occurred during receiving
+ if (len < 0) {
+ ESP_LOGE(TAG, "recv failed: errno %d", errno);
+ break;
+ }
+ // Data received
+ else {
+ rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
+ ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
+ ESP_LOGI(TAG, "%s", rx_buffer);
+ }
+ vTaskDelay(pdTICKS_TO_MS(100));
+ }
+
+ if (sock != -1) {
+ ESP_LOGE(TAG, "Shutting down socket and restarting...");
+ shutdown(sock, 0);
+ close(sock);
+ }
+ }
+}
+
+void app_main(void)
+{
// Init LED
rgb_init_LED(&MY_LED, 27, 12, 13);
rgb_set_color(&MY_LED, rgb_black);
- xTaskCreate(echo_task, "uart_echo_task", 3072, NULL, 10, NULL);
+ xTaskCreate(echo_task, "uart_echo", 3072, NULL, 10, NULL);
+
+ xTaskCreate(battery_task, "battery", 2048, NULL, 1, NULL);
- // Access point
- setup_softap();
+#if AP
+ setup_softap();
- setup_csi();
-
- for(;;) {
- vTaskDelay(10);
- }
+ setup_csi("F8:B3:B7:5A:34:F4");
- // Station
- /* setup_station(); */
+ for (int i = 0;i < 2;i++)
+ {
+ port_iterate = i;
+ xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET, 5, NULL);
+ vTaskDelay(pdTICKS_TO_MS(100));
+ }
+#else
+ setup_station();
- /* setup_csi(); */
-
- /* for(;;) { */
- /* socket_transmitter_sta_loop(&is_wifi_connected); */
- /* } */
+ // setup_csi("F0:24:F9:54:3B:89");
+
+ xTaskCreate(tcp_client_task, "tcp_client", 4096, (void*)AF_INET, 5, NULL);
+#endif
}
diff --git a/train.py b/train.py
new file mode 100644
index 0000000..8ed60c6
--- /dev/null
+++ b/train.py
@@ -0,0 +1,270 @@
+import sys
+
+if len(sys.argv) < 3:
+ print("Run script as: train.py [DATASET PATH] [MODEL SAVE NAME]")
+ exit()
+
+import tensorflow as tf
+from tensorflow import keras
+from tensorflow.keras import layers
+import matplotlib.pyplot as plt
+import pathlib
+
+# --- Ange din dataset-mapp här ---
+data_dir = pathlib.Path(f"{sys.argv[1]}/").with_suffix('')
+
+# --- Bildparametrar ---
+img_height = 192
+img_width = 50
+batch_size = 32
+
+# --- Ladda dataset från mappar ---
+train_ds = tf.keras.preprocessing.image_dataset_from_directory(
+ data_dir,
+ validation_split=0.2,
+ subset="training",
+ seed=123,
+ color_mode="grayscale",
+ image_size=(img_height, img_width),
+ batch_size=batch_size,
+ labels="inferred",
+ label_mode="int",
+ verbose=True
+)
+
+val_ds = tf.keras.preprocessing.image_dataset_from_directory(
+ data_dir,
+ validation_split=0.2,
+ subset="validation",
+ seed=123,
+ color_mode="grayscale",
+ image_size=(img_height, img_width),
+ batch_size=batch_size,
+ labels="inferred",
+ label_mode="int",
+ verbose=True
+)
+
+image_count = len(list(data_dir.glob('*/*.png'))) + len(list(data_dir.glob('p/*/*.png')))
+print(f"Number of images: {image_count}")
+
+class_names = train_ds.class_names
+num_classes = len(class_names)
+print(f"Class names: {class_names}")
+
+plt.figure(figsize=(20, 10))
+for images, labels in train_ds.take(1):
+ for i in range(9):
+ ax = plt.subplot(3, 3, i + 1)
+ plt.imshow(images[i].numpy().astype("uint8"), interpolation='nearest', aspect='auto', cmap='gray')
+ plt.title(class_names[labels[i]])
+ plt.axis("off")
+plt.suptitle("Training images", fontsize=30)
+plt.show()
+
+for image_batch, labels_batch in train_ds:
+ print(image_batch.shape)
+ print(labels_batch.shape)
+ break
+
+
+# --- Pipeline-optimering ---
+# AUTOTUNE = tf.data.AUTOTUNE
+# train_ds = train_ds.cache().shuffle(1000).prefetch(AUTOTUNE)
+# val_ds = val_ds.cache().prefetch(AUTOTUNE)
+
+# --- Data augmentation ---
+data_augmentation = tf.keras.Sequential([
+ layers.RandomFlip("horizontal"),
+ #layers.RandomZoom(0.2),
+ layers.RandomContrast(0.4)
+])
+
+plt.figure(figsize=(10, 10))
+for images, labels in train_ds.take(1):
+ for i in range(9):
+ # Add the image to a batch.
+ image = tf.cast(tf.expand_dims(images[i], 0), tf.float32)
+
+ augmented_image = data_augmentation(image)
+ ax = plt.subplot(3, 3, i + 1)
+ plt.imshow(augmented_image[0], interpolation='nearest', aspect='auto', cmap='gray')
+ plt.title(class_names[labels[i]])
+ plt.axis("off")
+plt.suptitle("Augmented training images", fontsize=30)
+plt.show()
+
+train_ds = train_ds.repeat(5).shuffle(1000)
+train_ds = train_ds.map(lambda x, y: (data_augmentation(x, training=True), y))
+
+# --- Modell ---
+model = keras.Sequential([
+ layers.Rescaling(1./255, input_shape=(img_height, img_width, 1), name="Input_image"),
+ # layers.Input(shape=(img_height, img_width, 1), name="Input_image"),
+
+ layers.Conv2D(32, (3, 3), activation="relu"),
+ layers.MaxPooling2D(),
+
+ layers.Conv2D(64, (3, 3), activation="relu"),
+ layers.MaxPooling2D(),
+
+ layers.Conv2D(128, (3, 3), activation="relu"),
+ layers.MaxPooling2D(),
+
+ layers.Flatten(),
+ layers.Dense(64, activation="relu"),
+ layers.Dense(num_classes, activation="softmax", name="Prediction")
+])
+
+earlystop = tf.keras.callbacks.EarlyStopping(monitor="val_loss", min_delta=0.1, patience=10, mode="min")
+
+model.compile(
+ optimizer=tf.keras.optimizers.Adam(learning_rate=0.0001),
+ loss=tf.keras.losses.SparseCategoricalCrossentropy(reduction="sum"),
+ metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
+)
+
+model.summary()
+
+keras.utils.plot_model(model, "model.png", show_shapes=True)
+
+# --- Train presence/no presence model ---
+history = model.fit(
+ train_ds,
+ validation_data=val_ds,
+ epochs=500,
+ callbacks=[earlystop]
+)
+
+# --- Plotta träning/validering ---
+plt.figure()
+plt.style.use('default')
+plt.plot(history.history["sparse_categorical_accuracy"], label="Training")
+plt.plot(history.history["val_sparse_categorical_accuracy"], label="Validation")
+plt.xlabel("Epochs")
+plt.ylabel("Accuracy")
+plt.legend()
+plt.show()
+
+model.save(f"{sys.argv[2]}.keras")
+
+# --- Activities ---
+
+activities_dir = pathlib.Path(f"{sys.argv[1]}/p/").with_suffix('')
+
+train_activities_ds = tf.keras.preprocessing.image_dataset_from_directory(
+ activities_dir,
+ validation_split=0.2,
+ subset="training",
+ seed=123,
+ color_mode="grayscale",
+ image_size=(img_height, img_width),
+ batch_size=batch_size,
+ labels="inferred",
+ label_mode="int",
+ verbose=True
+)
+
+val_activities_ds = tf.keras.preprocessing.image_dataset_from_directory(
+ activities_dir,
+ validation_split=0.2,
+ subset="validation",
+ seed=123,
+ color_mode="grayscale",
+ image_size=(img_height, img_width),
+ batch_size=batch_size,
+ labels="inferred",
+ label_mode="int",
+ verbose=True
+)
+
+image_count = len(list(data_dir.glob('p/*/*.png')))
+print(f"Number of images: {image_count}")
+
+class_names = train_activities_ds.class_names
+num_classes = len(class_names)
+print(f"Class names: {class_names}")
+
+plt.figure(figsize=(10, 10))
+for images, labels in train_activities_ds.take(1):
+ for i in range(9):
+ ax = plt.subplot(3, 3, i + 1)
+ plt.imshow(images[i].numpy().astype("uint8"), interpolation='nearest', aspect='auto', cmap='gray')
+ plt.title(class_names[labels[i]])
+ plt.axis("off")
+plt.suptitle("Training images", fontsize=30)
+plt.show()
+
+for image_batch, labels_batch in train_activities_ds:
+ print(image_batch.shape)
+ print(labels_batch.shape)
+ break
+
+# --- Pipeline-optimering ---
+# AUTOTUNE = tf.data.AUTOTUNE
+# train_ds = train_ds.cache().shuffle(1000).prefetch(AUTOTUNE)
+# val_ds = val_ds.cache().prefetch(AUTOTUNE)
+
+plt.figure(figsize=(10, 10))
+for images, labels in train_activities_ds.take(1):
+ for i in range(9):
+ # Add the image to a batch.
+ image = tf.cast(tf.expand_dims(images[i], 0), tf.float32)
+
+ augmented_image = data_augmentation(image)
+ ax = plt.subplot(3, 3, i + 1)
+ plt.imshow(augmented_image[0], interpolation='nearest', aspect='auto', cmap='gray')
+ plt.title(class_names[labels[i]])
+ plt.axis("off")
+plt.suptitle("Augmented training images", fontsize=30)
+plt.show()
+
+train_activities_ds = train_activities_ds.repeat(20).shuffle(1000)
+train_activities_ds = train_activities_ds.map(lambda x, y: (data_augmentation(x, training=True), y))
+
+# --- Modell ---
+activity_model = keras.Sequential([
+ layers.Rescaling(1./255, input_shape=(img_height, img_width, 1), name="Input_image"),
+ # layers.Input(shape=(img_height, img_width, 1), name="Input_image"),
+
+ layers.Conv2D(32, (3, 3), activation="relu"),
+ layers.MaxPooling2D(),
+
+ layers.Conv2D(64, (3, 3), activation="relu"),
+ layers.MaxPooling2D(),
+
+ layers.Conv2D(128, (3, 3), activation="relu"),
+ layers.MaxPooling2D(),
+
+ layers.Flatten(),
+ layers.Dense(64, activation="relu"),
+ layers.Dense(num_classes, activation="softmax", name="Prediction")
+])
+
+earlystop = tf.keras.callbacks.EarlyStopping(monitor="val_loss", min_delta=0.1, patience=10, mode="min")
+
+activity_model.compile(
+ optimizer=tf.keras.optimizers.Adam(learning_rate=0.0001),
+ loss=tf.keras.losses.SparseCategoricalCrossentropy(reduction="sum"),
+ metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
+)
+
+# --- Train activities model ---
+history = activity_model.fit(
+ train_activities_ds,
+ validation_data=val_activities_ds,
+ epochs=500,
+ callbacks=[earlystop]
+)
+
+# --- Plotta träning/validering ---
+plt.figure()
+plt.style.use('default')
+plt.plot(history.history["sparse_categorical_accuracy"], label="Training")
+plt.plot(history.history["val_sparse_categorical_accuracy"], label="Validation")
+plt.xlabel("Epochs")
+plt.ylabel("Accuracy")
+plt.legend()
+plt.show()
+
+activity_model.save(f"{sys.argv[2]}_activities.keras")