diff options
| -rw-r--r-- | README.md | 3 | ||||
| m--------- | components/EZWIFI | 0 | ||||
| -rw-r--r-- | get_data.py | 60 | ||||
| -rw-r--r-- | gui.py | 154 | ||||
| -rw-r--r-- | main/CDIO.c | 328 | ||||
| -rw-r--r-- | train.py | 270 |
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 @@ -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") |
