EC2 Default User
commited on
Commit
•
ca291e1
1
Parent(s):
2a9402e
First commit: Host stm32ai modelzoo
Browse files- Dockerfile +39 -0
- LICENCE.md +3 -0
- README.md +3 -3
- assets/ST_logo_2024_white.png +0 -0
- assets/github-mark-white.png +0 -0
- assets/style.css +78 -0
- dash_app.py +1109 -0
- datasets/README.md +6 -0
- download_datasets.py +67 -0
- models/README.md +6 -0
- requirements_dash.txt +9 -0
- static/ST_logo_2024_blue.jpg +0 -0
- templates/index.html +211 -0
Dockerfile
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.10-slim
|
2 |
+
|
3 |
+
RUN apt-get update && apt-get install -y \
|
4 |
+
build-essential \
|
5 |
+
libgl1 \
|
6 |
+
libglib2.0-0 \
|
7 |
+
wget \
|
8 |
+
git \
|
9 |
+
&& rm -rf /var/lib/apt/lists/*
|
10 |
+
|
11 |
+
RUN useradd -ms /bin/bash appuser
|
12 |
+
USER appuser
|
13 |
+
|
14 |
+
ENV HOME=/home/appuser \
|
15 |
+
PATH=/home/appuser/.local/bin:$PATH \
|
16 |
+
STATS_TYPE='HuggingFace_devcloud' \
|
17 |
+
PYTHONDONTWRITEBYTECODE=1 \
|
18 |
+
PYTHONUNBUFFERED=1
|
19 |
+
|
20 |
+
WORKDIR $HOME/app
|
21 |
+
|
22 |
+
RUN pip install --no-cache-dir --upgrade pip
|
23 |
+
|
24 |
+
COPY --chown=appuser . $HOME/app
|
25 |
+
|
26 |
+
#For training and benchmarking clone modelzoo-services
|
27 |
+
RUN git clone https://github.com/STMicroelectronics/stm32ai-modelzoo-services.git
|
28 |
+
|
29 |
+
#To benchmark pre-trained models from stm32ai-modelzoo clone this repo
|
30 |
+
#RUN git clone https://github.com/STMicroelectronics/stm32ai-modelzoo.git
|
31 |
+
|
32 |
+
COPY --chown=appuser download_datasets.py $HOME/app/download_datasets.py
|
33 |
+
|
34 |
+
RUN pip install --no-cache-dir -r requirements_dash.txt
|
35 |
+
RUN pip install --no-cache-dir -r stm32ai-modelzoo-services/requirements.txt
|
36 |
+
|
37 |
+
EXPOSE 7860
|
38 |
+
|
39 |
+
CMD ["sh", "-c", "python download_datasets.py && python dash_app.py"]
|
LICENCE.md
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# Copyright (c) 2017 STMicroelectronics
|
2 |
+
|
3 |
+
This software component is licensed by STMicroelectronics under the **BSD 3-Clause** license. You may not use this file except in compliance with this license. You may obtain a copy of the license [here](https://opensource.org/licenses/BSD-3-Clause).
|
README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
---
|
2 |
-
title:
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
colorTo: gray
|
6 |
sdk: docker
|
7 |
pinned: false
|
|
|
1 |
---
|
2 |
+
title: stm32 model zoo app
|
3 |
+
emoji: 🚀
|
4 |
+
colorFrom: blue
|
5 |
colorTo: gray
|
6 |
sdk: docker
|
7 |
pinned: false
|
assets/ST_logo_2024_white.png
ADDED
assets/github-mark-white.png
ADDED
assets/style.css
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
html, body {
|
2 |
+
font-family: 'Arial', sans-serif;
|
3 |
+
background-color: #fcfcfc;
|
4 |
+
color: #333;
|
5 |
+
margin: 0;
|
6 |
+
padding: 0;
|
7 |
+
}
|
8 |
+
|
9 |
+
.top-bar {
|
10 |
+
display: flex;
|
11 |
+
justify-content: space-between;
|
12 |
+
align-items: center;
|
13 |
+
width: 100%;
|
14 |
+
background-color: #03234b;
|
15 |
+
color: white;
|
16 |
+
padding: 7px 5px;
|
17 |
+
font-size: 18px;
|
18 |
+
font-weight: bold;
|
19 |
+
border-bottom: 2px solid #03234b;
|
20 |
+
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
21 |
+
}
|
22 |
+
|
23 |
+
.credentials-section {
|
24 |
+
display: none;
|
25 |
+
background-color: #f8f9fa;
|
26 |
+
border: 1px solid #e0e0e0;
|
27 |
+
padding: 20px;
|
28 |
+
border-radius: 8px;
|
29 |
+
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
30 |
+
}
|
31 |
+
|
32 |
+
|
33 |
+
.credentials-col {
|
34 |
+
padding: 10px;
|
35 |
+
}
|
36 |
+
|
37 |
+
|
38 |
+
.credentials-text {
|
39 |
+
font-size: 15px;
|
40 |
+
font-weight: bold;
|
41 |
+
color: #03234b;
|
42 |
+
}
|
43 |
+
|
44 |
+
.input-field {
|
45 |
+
display: block;
|
46 |
+
width: 100%;
|
47 |
+
padding: 8px;
|
48 |
+
margin-bottom: 10px;
|
49 |
+
font-size: 14px;
|
50 |
+
border: 1px solid #ced4da;
|
51 |
+
border-radius: 4px;
|
52 |
+
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
53 |
+
}
|
54 |
+
|
55 |
+
|
56 |
+
.input-field:focus {
|
57 |
+
border-color: #357ab7;
|
58 |
+
outline: none;
|
59 |
+
box-shadow: 0 0 5px rgba(53, 122, 183, 0.5);
|
60 |
+
}
|
61 |
+
|
62 |
+
|
63 |
+
.start-button {
|
64 |
+
display: block;
|
65 |
+
width: 100%;
|
66 |
+
background-color: #03234b;
|
67 |
+
color: #ffffff;
|
68 |
+
font-size: 14px;
|
69 |
+
padding: 10px;
|
70 |
+
border-radius: 4px;
|
71 |
+
border: none;
|
72 |
+
cursor: pointer;
|
73 |
+
transition: background-color 0.3s ease;
|
74 |
+
}
|
75 |
+
|
76 |
+
.start-button:hover {
|
77 |
+
background-color: #3cb4e6;
|
78 |
+
}
|
dash_app.py
ADDED
@@ -0,0 +1,1109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# /*---------------------------------------------------------------------------------------------
|
2 |
+
# * Copyright (c) 2024 STMicroelectronics.
|
3 |
+
# * All rights reserved.
|
4 |
+
# *
|
5 |
+
# * This software is licensed under terms that can be found in the LICENSE file in
|
6 |
+
# * the root directory of this software component.
|
7 |
+
# * If no LICENSE file comes with this software, it is provided AS-IS.
|
8 |
+
# *--------------------------------------------------------------------------------------------*/
|
9 |
+
import os
|
10 |
+
import re
|
11 |
+
import uuid
|
12 |
+
import time
|
13 |
+
import shutil
|
14 |
+
import zipfile
|
15 |
+
import threading
|
16 |
+
import subprocess
|
17 |
+
import select
|
18 |
+
from datetime import datetime
|
19 |
+
from concurrent.futures import ThreadPoolExecutor
|
20 |
+
|
21 |
+
import dash
|
22 |
+
from dash import dcc, html
|
23 |
+
from dash.dependencies import Input, Output, State, ALL
|
24 |
+
import dash_bootstrap_components as dbc
|
25 |
+
from dash.exceptions import PreventUpdate
|
26 |
+
|
27 |
+
from flask import Flask, render_template, request, send_file, jsonify
|
28 |
+
|
29 |
+
import yaml
|
30 |
+
import ruamel.yaml
|
31 |
+
import pandas as pd
|
32 |
+
|
33 |
+
|
34 |
+
import logging
|
35 |
+
# Configure logging
|
36 |
+
logging.basicConfig(level=logging.DEBUG)
|
37 |
+
logger = logging.getLogger(__name__)
|
38 |
+
|
39 |
+
|
40 |
+
server = Flask(__name__)
|
41 |
+
server.secret_key = os.urandom(24)
|
42 |
+
|
43 |
+
|
44 |
+
@server.route('/')
|
45 |
+
def welcome_page():
|
46 |
+
"""
|
47 |
+
Handles the welcome page route.
|
48 |
+
|
49 |
+
This function extracts the username from the request host,
|
50 |
+
determines if the duplicate mode should be enabled, and renders
|
51 |
+
the welcome page template with the duplicate mode state.
|
52 |
+
|
53 |
+
Returns:
|
54 |
+
str: The rendered 'index.html' template with the duplicate_mode parameter.
|
55 |
+
"""
|
56 |
+
host = request.host
|
57 |
+
print("host:", host)
|
58 |
+
usr_match = re.match(r'^(.*?)\-stm32', host)
|
59 |
+
print("usr_match:", usr_match)
|
60 |
+
|
61 |
+
if usr_match:
|
62 |
+
hf_user = usr_match.group(1)
|
63 |
+
else:
|
64 |
+
hf_user = "modelzoo_user"
|
65 |
+
|
66 |
+
if hf_user == "stmicroelectronics":
|
67 |
+
duplicate_mode = True
|
68 |
+
else:
|
69 |
+
duplicate_mode = False
|
70 |
+
|
71 |
+
print("hf_user:", hf_user)
|
72 |
+
print("duplicate_mode:", duplicate_mode)
|
73 |
+
|
74 |
+
return render_template('index.html', duplicate_mode=duplicate_mode)
|
75 |
+
|
76 |
+
|
77 |
+
external_stylesheets = [dbc.themes.LITERA]
|
78 |
+
app = dash.Dash(__name__, server=server,external_stylesheets=external_stylesheets, url_base_pathname='/dash_app/', suppress_callback_exceptions=True)
|
79 |
+
|
80 |
+
|
81 |
+
local_yamls = {
|
82 |
+
'image_classification': 'stm32ai-modelzoo-services/image_classification/src/user_config.yaml',
|
83 |
+
'human_activity_recognition': 'stm32ai-modelzoo-services/human_activity_recognition/src/user_config.yaml',
|
84 |
+
'hand_posture': 'stm32ai-modelzoo-services/hand_posture/src/user_config.yaml',
|
85 |
+
'object_detection': 'stm32ai-modelzoo-services/object_detection/src/user_config.yaml',
|
86 |
+
'audio_event_detection': 'stm32ai-modelzoo-services/audio_event_detection/src/user_config.yaml',
|
87 |
+
'pose_estimation': 'stm32ai-modelzoo-services/pose_estimation/src/user_config.yaml',
|
88 |
+
'semantic_segmentation': 'stm32ai-modelzoo-services/semantic_segmentation/src/user_config.yaml'
|
89 |
+
}
|
90 |
+
|
91 |
+
|
92 |
+
def banner():
|
93 |
+
return html.Div(
|
94 |
+
id="banner",
|
95 |
+
className="top-bar",
|
96 |
+
style={
|
97 |
+
"display": "flex",
|
98 |
+
"align-items": "center",
|
99 |
+
"justify-content": "space-between",
|
100 |
+
"position": "fixed",
|
101 |
+
"top": "0",
|
102 |
+
"left": "0",
|
103 |
+
"width": "100%",
|
104 |
+
"z-index": "1000",
|
105 |
+
"background-color": "#3234b",
|
106 |
+
"padding": "10px 20px",
|
107 |
+
"box-shadow": "0px 2px 4px rgba(0, 0, 0, 0.1)"
|
108 |
+
},
|
109 |
+
children=[
|
110 |
+
html.A(
|
111 |
+
id="learn-more-button",
|
112 |
+
children=[
|
113 |
+
html.Img(
|
114 |
+
src=app.get_asset_url("github-mark-white.png"),
|
115 |
+
style={"width": "20px", "height": "20px", "margin-right": "10px"}
|
116 |
+
),
|
117 |
+
"stm32ai-modelzoo",
|
118 |
+
],
|
119 |
+
href="https://github.com/STMicroelectronics/stm32ai-modelzoo-services",
|
120 |
+
target="_blank",
|
121 |
+
style={
|
122 |
+
"display": "flex",
|
123 |
+
"align-items": "center",
|
124 |
+
"color": "#ffffff",
|
125 |
+
"text-decoration": "none",
|
126 |
+
"font-size": "15px",
|
127 |
+
"font-family": "Arial, sans-serif"
|
128 |
+
}
|
129 |
+
),
|
130 |
+
html.Div(
|
131 |
+
html.Img(
|
132 |
+
id="logo",
|
133 |
+
src=app.get_asset_url("ST_logo_2024_white.png"),
|
134 |
+
style={"width": "50px", "height": "auto"}
|
135 |
+
),
|
136 |
+
style={"text-align": "center"}
|
137 |
+
),
|
138 |
+
html.Div(
|
139 |
+
[
|
140 |
+
html.A(
|
141 |
+
[
|
142 |
+
html.H5(
|
143 |
+
"ST Edge AI Developer Cloud",
|
144 |
+
style={
|
145 |
+
"margin": "0",
|
146 |
+
"text-align": "right",
|
147 |
+
"color": "#ffffff",
|
148 |
+
"font-size": "15px",
|
149 |
+
"font-weight": "bold",
|
150 |
+
"font-family": "Arial, sans-serif"
|
151 |
+
}
|
152 |
+
)
|
153 |
+
],
|
154 |
+
href="https://stm32ai-cs.st.com/home",
|
155 |
+
target="_blank",
|
156 |
+
style={
|
157 |
+
"display": "flex",
|
158 |
+
"align-items": "center",
|
159 |
+
"text-decoration": "none"
|
160 |
+
}
|
161 |
+
)
|
162 |
+
],
|
163 |
+
style={"padding-right": "10px"}
|
164 |
+
)
|
165 |
+
]
|
166 |
+
)
|
167 |
+
|
168 |
+
|
169 |
+
def read_configs(selected_model):
|
170 |
+
"""
|
171 |
+
Loads a YAML file based on the selected model by the user.
|
172 |
+
|
173 |
+
Args:
|
174 |
+
selected_model (str): The key to select the appropriate YAML file path.
|
175 |
+
|
176 |
+
Returns:
|
177 |
+
dict: The loaded YAML data.
|
178 |
+
"""
|
179 |
+
if not selected_model:
|
180 |
+
raise ValueError("No model selected. Please select a valid model.")
|
181 |
+
if selected_model not in local_yamls:
|
182 |
+
raise ValueError(f"Model '{selected_model}' not found in local_yamls")
|
183 |
+
|
184 |
+
yaml_path = local_yamls[selected_model]
|
185 |
+
try:
|
186 |
+
with open(yaml_path, 'r') as file:
|
187 |
+
return yaml.safe_load(file)
|
188 |
+
except Exception as e:
|
189 |
+
raise ValueError(f"Error reading YAML file at {yaml_path}: {e}")
|
190 |
+
|
191 |
+
|
192 |
+
def build_yaml_form(yaml_content, parent_key=''):
|
193 |
+
"""
|
194 |
+
Recursively builds a form based on the provided YAML content.
|
195 |
+
|
196 |
+
Parameters:
|
197 |
+
- yaml_content (dict): The YAML content to build the form from.
|
198 |
+
- parent_key (str): The parent key to maintain the hierarchy of nested keys. Default is an empty string.
|
199 |
+
|
200 |
+
Returns:
|
201 |
+
- list: A list of Dash Bootstrap Components (dbc) AccordionItems representing the form fields.
|
202 |
+
"""
|
203 |
+
accordion_items = []
|
204 |
+
for key, value in yaml_content.items():
|
205 |
+
full_key = f"{parent_key}.{key}" if parent_key else key
|
206 |
+
|
207 |
+
if isinstance(value, dict):
|
208 |
+
nested_accordion = build_yaml_form(value, full_key)
|
209 |
+
accordion_items.append(
|
210 |
+
dbc.AccordionItem(
|
211 |
+
nested_accordion,
|
212 |
+
title=key.capitalize()
|
213 |
+
)
|
214 |
+
)
|
215 |
+
else:
|
216 |
+
|
217 |
+
field = [html.Label(key, style={"font-weight": "bold", "margin-bottom": "5px"})]
|
218 |
+
|
219 |
+
if isinstance(value, bool):
|
220 |
+
field.append(
|
221 |
+
dcc.Checklist(
|
222 |
+
id={'type': 'yaml-setting', 'index': full_key},
|
223 |
+
options=[{'label': '', 'value': True}],
|
224 |
+
value=[True] if value else [],
|
225 |
+
style={"padding": "10px", "border": "1px solid #ddd", "margin-bottom": "10px"}
|
226 |
+
)
|
227 |
+
)
|
228 |
+
elif isinstance(value, list):
|
229 |
+
field.append(
|
230 |
+
dcc.Dropdown(
|
231 |
+
id={'type': 'yaml-setting', 'index': full_key},
|
232 |
+
options=[{'label': str(v), 'value': v} for v in value],
|
233 |
+
value=value,
|
234 |
+
multi=True,
|
235 |
+
style={"padding": "10px", "border": "1px solid #ddd", "margin-bottom": "10px"}
|
236 |
+
)
|
237 |
+
)
|
238 |
+
else:
|
239 |
+
field.append(
|
240 |
+
dcc.Input(
|
241 |
+
id={'type': 'yaml-setting', 'index': full_key},
|
242 |
+
value=value,
|
243 |
+
type='text',
|
244 |
+
style={"padding": "10px", "border": "1px solid #ddd", "margin-bottom": "10px"}
|
245 |
+
)
|
246 |
+
)
|
247 |
+
|
248 |
+
accordion_items.append(
|
249 |
+
dbc.AccordionItem(
|
250 |
+
field,
|
251 |
+
title=key.capitalize()
|
252 |
+
)
|
253 |
+
)
|
254 |
+
|
255 |
+
return accordion_items
|
256 |
+
|
257 |
+
|
258 |
+
def create_yaml(yaml_content):
|
259 |
+
"""
|
260 |
+
Creates a YAML form using Dash Bootstrap Components (dbc) and Dash HTML Components (html).
|
261 |
+
|
262 |
+
Parameters:
|
263 |
+
yaml_content (dict): The content of the YAML file to be used for building the form.
|
264 |
+
|
265 |
+
Returns:
|
266 |
+
dbc.Form: A Dash form component containing an accordion with the YAML content and a submit button.
|
267 |
+
"""
|
268 |
+
accordion_items = build_yaml_form(yaml_content)
|
269 |
+
accordion = dbc.Accordion(
|
270 |
+
accordion_items,
|
271 |
+
start_collapsed=True
|
272 |
+
)
|
273 |
+
|
274 |
+
return dbc.Form([
|
275 |
+
accordion,
|
276 |
+
html.Div(
|
277 |
+
dbc.Button(
|
278 |
+
'Submit',
|
279 |
+
id='apply-button',
|
280 |
+
style={
|
281 |
+
'background-color': '#FFD200',
|
282 |
+
'color': '#03234b',
|
283 |
+
'font-size': '14px',
|
284 |
+
'padding': '10px 10px 10px 10px',
|
285 |
+
'border-radius': '5px',
|
286 |
+
'margin-top': '15px',
|
287 |
+
'border': '2px solid #FFD200',
|
288 |
+
'box-shadow': '0px 4px 6px rgba(0, 0, 0, 0.1)',
|
289 |
+
}
|
290 |
+
),
|
291 |
+
style={
|
292 |
+
'display': 'flex',
|
293 |
+
'justify-content': 'center',
|
294 |
+
'margin-top': '15px',
|
295 |
+
}
|
296 |
+
),
|
297 |
+
html.Div(
|
298 |
+
id='submission-outcome',
|
299 |
+
style={
|
300 |
+
'marginTop': '10px',
|
301 |
+
'textAlign': 'center',
|
302 |
+
'fontStyle': 'italic',
|
303 |
+
'color': '#03234b',
|
304 |
+
'font-size': '14px'
|
305 |
+
}
|
306 |
+
)
|
307 |
+
])
|
308 |
+
|
309 |
+
|
310 |
+
def process_form_configs(form_configs):
|
311 |
+
"""
|
312 |
+
Extracts and processes form data to update YAML content.
|
313 |
+
|
314 |
+
This function processes the form data, converting values to appropriate types
|
315 |
+
and updating the YAML content accordingly.
|
316 |
+
|
317 |
+
Args:
|
318 |
+
form_configs (dict): The form data to be processed.
|
319 |
+
|
320 |
+
Returns:
|
321 |
+
dict: The updated YAML content with processed form data.
|
322 |
+
"""
|
323 |
+
updated_yaml = {}
|
324 |
+
for key, value in form_configs.items():
|
325 |
+
if value is not None:
|
326 |
+
if isinstance(value, list) and len(value) == 1:
|
327 |
+
value = value[0]
|
328 |
+
|
329 |
+
if isinstance(value, str):
|
330 |
+
try:
|
331 |
+
if '.' in value:
|
332 |
+
value = float(value)
|
333 |
+
else:
|
334 |
+
value = int(value)
|
335 |
+
except ValueError:
|
336 |
+
pass
|
337 |
+
|
338 |
+
updated_yaml[key] = value
|
339 |
+
|
340 |
+
return updated_yaml
|
341 |
+
|
342 |
+
|
343 |
+
def create_archive(archive_path, directory_to_compress):
|
344 |
+
"""
|
345 |
+
Creates a ZIP archive of a specified directory.
|
346 |
+
|
347 |
+
Parameters:
|
348 |
+
archive_path (str): The path where the ZIP archive will be created.
|
349 |
+
directory_to_compress (str): The directory whose contents will be compressed into the ZIP archive.
|
350 |
+
|
351 |
+
Returns:
|
352 |
+
None
|
353 |
+
"""
|
354 |
+
def add_file_to_zip(zipf, file_path, arcname):
|
355 |
+
"""
|
356 |
+
Adds a file to the ZIP archive.
|
357 |
+
|
358 |
+
Parameters:
|
359 |
+
zipf (zipfile.ZipFile): The ZIP file object.
|
360 |
+
file_path (str): The path of the file to add to the ZIP archive.
|
361 |
+
arcname (str): The archive name for the file within the ZIP archive.
|
362 |
+
|
363 |
+
Returns:
|
364 |
+
None
|
365 |
+
"""
|
366 |
+
zipf.write(file_path, arcname=arcname)
|
367 |
+
|
368 |
+
with zipfile.ZipFile(archive_path, 'w', compression=zipfile.ZIP_DEFLATED) as zipf:
|
369 |
+
with ThreadPoolExecutor() as executor:
|
370 |
+
for root_dir, sub_dirs, files in os.walk(directory_to_compress):
|
371 |
+
for file_name in files:
|
372 |
+
file_path = os.path.join(root_dir, file_name)
|
373 |
+
if os.path.abspath(file_path) != os.path.abspath(archive_path):
|
374 |
+
arcname = os.path.relpath(file_path, directory_to_compress)
|
375 |
+
executor.submit(add_file_to_zip, zipf, file_path, arcname)
|
376 |
+
|
377 |
+
|
378 |
+
def create_dashboard_layout():
|
379 |
+
"""
|
380 |
+
Creates the layout for the application: STM32ModelZoo dashboard.
|
381 |
+
|
382 |
+
This function defines the structure and components of the dashboard,
|
383 |
+
including the banner, model selection dropdown, YAML update options,
|
384 |
+
credentials input, output display, training metrics graphs, and download button.
|
385 |
+
|
386 |
+
Returns:
|
387 |
+
dbc.Container: A Dash Bootstrap Component container with the dashboard layout.
|
388 |
+
"""
|
389 |
+
return html.Div([
|
390 |
+
banner(),
|
391 |
+
dbc.Container([
|
392 |
+
dcc.Location(id='url', refresh=False),
|
393 |
+
dbc.Row(dbc.Col(html.H3("STM32 Modelzoo", style={'color': '#03234b', 'text-align': 'center',"margin-top": "80px", "font-family": "Arial, sans-serif"}), className="mb-4")),
|
394 |
+
dbc.Row([
|
395 |
+
dbc.Col(
|
396 |
+
html.H5("Use case selection", style={'color': '#03234b', 'margin-bottom': '10px'}),
|
397 |
+
width=12
|
398 |
+
)
|
399 |
+
], id="use-case-section", style={"display": "none"}),
|
400 |
+
dbc.Row(dbc.Col(dcc.Dropdown(
|
401 |
+
id='selected-model',
|
402 |
+
options=[
|
403 |
+
{'label': 'Image Classification (IC)', 'value': 'image_classification'},
|
404 |
+
{'label': 'Human Activity Recognition (HAR)', 'value': 'human_activity_recognition'},
|
405 |
+
{'label': 'Hand Posture', 'value': 'hand_posture'},
|
406 |
+
{'label': 'Audio Event Detection(AED)', 'value': 'audio_event_detection'},
|
407 |
+
{'label': 'Object Detection', 'value': 'object_detection'},
|
408 |
+
{'label': 'Pose estimation', 'value': 'pose_estimation'},
|
409 |
+
{'label': 'Semantic Segmentation', 'value': 'semantic_segmentation'},
|
410 |
+
],
|
411 |
+
placeholder="Please select your use case",
|
412 |
+
className="mb-4"
|
413 |
+
))),
|
414 |
+
|
415 |
+
dbc.Row(
|
416 |
+
dbc.Col(
|
417 |
+
html.Div(
|
418 |
+
id='toggle-yaml',
|
419 |
+
children=[
|
420 |
+
html.P("Please update the YAML file: Dataset path(exemple: ../datasets/your_use_case/name_of_dataset) or datasets/your_prepared_dataset"),
|
421 |
+
dcc.RadioItems(
|
422 |
+
id='modify-yaml-choice',
|
423 |
+
labelStyle={'display': 'inline-block', 'margin-right': '10px'},
|
424 |
+
className="mb-4",
|
425 |
+
),
|
426 |
+
dcc.Upload(
|
427 |
+
id='load-yaml-file',
|
428 |
+
children=html.Button('Upload YAML File'),
|
429 |
+
style={'display': 'none'}
|
430 |
+
),
|
431 |
+
html.Div(id='load-state', style={'margin-top': '10px'}),
|
432 |
+
html.Div(id='yaml-layout', style={'display': 'none'})
|
433 |
+
],
|
434 |
+
style={'font-family': 'Arial, sans-serif', 'display': 'none'}
|
435 |
+
)
|
436 |
+
)
|
437 |
+
),
|
438 |
+
dbc.Row([
|
439 |
+
dbc.Col([
|
440 |
+
html.P("Enter your ST Edge AI Developer Cloud credentials:", style={'color': '03234b', 'fontSize': '15px', 'fontWeight': 'bold'}, className="credentials-text"),
|
441 |
+
dcc.Input(id='devcloud-username-input', type='text', placeholder='Enter username', className="input-field mb-2"),
|
442 |
+
dcc.Input(id='devcloud-password-input', type='password', placeholder='Enter password', className="input-field mb-4")
|
443 |
+
], width=6),
|
444 |
+
dbc.Col([
|
445 |
+
dbc.Button('Launch training', id='process-button', color="#3234b", className="start-button mb-4", style={'display': 'none', 'box-shadow': '0px 4px 6px rgba(0, 0, 0, 0.1)'})
|
446 |
+
], className="credentials-col")
|
447 |
+
], id='credentials-section', style={
|
448 |
+
'display': 'none',
|
449 |
+
'justify-content': 'center',
|
450 |
+
'align-items': 'center',
|
451 |
+
'height': '100vh',
|
452 |
+
}, className="credentials-section mb-4"),
|
453 |
+
|
454 |
+
dbc.Row([
|
455 |
+
dbc.Col(
|
456 |
+
html.H5("Results visualization", style={'color': '#03234b', 'margin-bottom': '10px'}),
|
457 |
+
width=12
|
458 |
+
)
|
459 |
+
], id="results-section", style={"display": "none"}),
|
460 |
+
dbc.Row([
|
461 |
+
dbc.Col(dbc.Card([
|
462 |
+
dbc.CardHeader("Command output"),
|
463 |
+
dbc.CardBody(
|
464 |
+
html.Div(id='log-reader', style={'whiteSpace': 'pre-wrap', 'padding-top': '15px', 'height': '100%', 'overflow': 'auto'}),
|
465 |
+
style={'height': '300px'}
|
466 |
+
)
|
467 |
+
]))
|
468 |
+
],style={'margin-bottom': '30px'}),
|
469 |
+
dbc.Row([
|
470 |
+
dbc.Col(dbc.Card([
|
471 |
+
dbc.CardHeader("Metrics plot"),
|
472 |
+
dbc.CardBody(
|
473 |
+
dcc.Graph(id='acc-visualization'),
|
474 |
+
style={'height': '400px','overflow':'auto'}
|
475 |
+
)
|
476 |
+
]))
|
477 |
+
],style={'margin-bottom': '30px'}),
|
478 |
+
dbc.Row([
|
479 |
+
dbc.Col(dbc.Card([
|
480 |
+
dbc.CardHeader("Metrics plot"),
|
481 |
+
dbc.CardBody(
|
482 |
+
dcc.Graph(id='loss-visualization'),
|
483 |
+
style={'height': '400px','overflow':'auto'}
|
484 |
+
)
|
485 |
+
]))
|
486 |
+
]),
|
487 |
+
dcc.Interval(id='interval-widget', interval=1000, n_intervals=0),
|
488 |
+
dcc.Download(id="download-resource"),
|
489 |
+
dbc.Button('Download outputs', id='download-action', className="mb-4", style={
|
490 |
+
'display': 'flex',
|
491 |
+
'justifyContent': 'center',
|
492 |
+
'alignItems': 'center',
|
493 |
+
'background-color': '#ffd200',
|
494 |
+
'color': '#ffffff',
|
495 |
+
'font-size': '14px',
|
496 |
+
'padding': '10px 10px',
|
497 |
+
'border-radius': '5px',
|
498 |
+
'margin-top': '15px',
|
499 |
+
'box-shadow': '0px 4px 6px rgba(0, 0, 0, 0.1)',
|
500 |
+
}),
|
501 |
+
], fluid=True)
|
502 |
+
])
|
503 |
+
|
504 |
+
app.layout = create_dashboard_layout
|
505 |
+
|
506 |
+
logs = []
|
507 |
+
lock = threading.Lock()
|
508 |
+
new_training = False
|
509 |
+
|
510 |
+
def fill_logs(message):
|
511 |
+
"""
|
512 |
+
Appends a message to the logs list in a thread-safe manner.
|
513 |
+
|
514 |
+
Parameters:
|
515 |
+
message (str): The message to be appended to the logs.
|
516 |
+
|
517 |
+
Returns:
|
518 |
+
None
|
519 |
+
"""
|
520 |
+
with lock:
|
521 |
+
logs.append(message)
|
522 |
+
|
523 |
+
def run_script(script, devcloud_username, devcloud_password):
|
524 |
+
"""
|
525 |
+
Executes a given script with the provided ST Developer Cloud credentials and logs the output.
|
526 |
+
|
527 |
+
Parameters:
|
528 |
+
- script (str): The path to the script to be executed.
|
529 |
+
- devcloud_username (str): Username for ST Developer Cloud.
|
530 |
+
- devcloud_password (str): Password for ST Developer Cloud.
|
531 |
+
|
532 |
+
Returns:
|
533 |
+
- None
|
534 |
+
"""
|
535 |
+
global logs
|
536 |
+
|
537 |
+
with lock:
|
538 |
+
logs = []
|
539 |
+
|
540 |
+
os.environ['stmai_username'] = devcloud_username
|
541 |
+
os.environ['stmai_password'] = devcloud_password
|
542 |
+
os.environ['STATS_TYPE'] = 'HuggingFace_devcloud'
|
543 |
+
|
544 |
+
execution = subprocess.Popen(['python3', script], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
545 |
+
while True:
|
546 |
+
file_descriptors = [execution.stdout.fileno(), execution.stderr.fileno()]
|
547 |
+
selected_descriptors = select.select(file_descriptors, [], [])
|
548 |
+
|
549 |
+
for descriptor in selected_descriptors[0]:
|
550 |
+
if descriptor == execution.stdout.fileno():
|
551 |
+
out = execution.stdout.readline()
|
552 |
+
if out:
|
553 |
+
fill_logs(out)
|
554 |
+
if out == '' and execution.poll() is not None:
|
555 |
+
return
|
556 |
+
if descriptor == execution.stderr.fileno():
|
557 |
+
error = execution.stderr.readline()
|
558 |
+
if error:
|
559 |
+
fill_logs(error)
|
560 |
+
|
561 |
+
def execute_async(script, devcloud_username, devcloud_password):
|
562 |
+
"""
|
563 |
+
Executes a Python script asynchronously in a separate thread.
|
564 |
+
|
565 |
+
Parameters:
|
566 |
+
script (str): The path to the Python script to be executed.
|
567 |
+
devcloud_username (str): The username for the DevCloud environment.
|
568 |
+
devcloud_password (str): The password for the DevCloud environment.
|
569 |
+
|
570 |
+
Returns:
|
571 |
+
None
|
572 |
+
"""
|
573 |
+
thread = threading.Thread(target=run_script, args=(script, devcloud_username, devcloud_password))
|
574 |
+
thread.start()
|
575 |
+
|
576 |
+
|
577 |
+
@app.callback(
|
578 |
+
Output("config-section", "style"),
|
579 |
+
Input('selected-model', 'value')
|
580 |
+
)
|
581 |
+
def toggle_config_section(selected_model):
|
582 |
+
"""
|
583 |
+
Toggles the visibility of the configuration section based on the selected model.
|
584 |
+
|
585 |
+
Parameters:
|
586 |
+
selected_model (str): The value of the selected model from the dropdown.
|
587 |
+
|
588 |
+
Returns:
|
589 |
+
dict: A dictionary containing the CSS style for the configuration section.
|
590 |
+
"""
|
591 |
+
if selected_model:
|
592 |
+
return {"display": "block"}
|
593 |
+
else:
|
594 |
+
return {"display": "none"}
|
595 |
+
|
596 |
+
|
597 |
+
@app.callback(
|
598 |
+
Output('toggle-yaml', 'style'),
|
599 |
+
Input('selected-model', 'value')
|
600 |
+
)
|
601 |
+
def dipslay_yaml_container(selected_model):
|
602 |
+
"""
|
603 |
+
Toggles the display of the YAML update container based on the selected model.
|
604 |
+
|
605 |
+
This function updates the CSS style of the YAML update container to either
|
606 |
+
show or hide it based on whether a model is selected from the dropdown.
|
607 |
+
|
608 |
+
Args:
|
609 |
+
selected_model (str): The selected model from the dropdown.
|
610 |
+
|
611 |
+
Returns:
|
612 |
+
dict: A dictionary containing the CSS style to either display or hide the container.
|
613 |
+
"""
|
614 |
+
if selected_model:
|
615 |
+
return {'display': 'block'}
|
616 |
+
return {'display': 'none'}
|
617 |
+
|
618 |
+
@app.callback(
|
619 |
+
[Output('yaml-layout', 'style'),
|
620 |
+
Output('yaml-layout', 'children')],
|
621 |
+
[Input('modify-yaml-choice', 'value'),
|
622 |
+
Input('selected-model', 'value')]
|
623 |
+
)
|
624 |
+
|
625 |
+
def display_yaml_form(selection_update, selected_model):
|
626 |
+
"""
|
627 |
+
Toggles the display of the YAML form and updates its content based on user input.
|
628 |
+
|
629 |
+
This function updates the CSS style and content of the YAML form based on whether
|
630 |
+
the user chooses to update the YAML file and a model is selected from the dropdown.
|
631 |
+
|
632 |
+
Args:
|
633 |
+
selection_update (str): The user's choice to update the YAML file ('yes' or 'no').
|
634 |
+
selected_model (str): The selected model from the dropdown.
|
635 |
+
|
636 |
+
Returns:
|
637 |
+
tuple: A tuple containing the CSS style to either display or hide the form,
|
638 |
+
and the form content generated from the YAML data.
|
639 |
+
"""
|
640 |
+
|
641 |
+
if not selected_model:
|
642 |
+
return {'display': 'none'}, "Please select a model to display its configuration."
|
643 |
+
|
644 |
+
try:
|
645 |
+
yaml_conf = read_configs(selected_model)
|
646 |
+
form_conf = create_yaml(yaml_conf)
|
647 |
+
return {'display': 'block'}, form_conf
|
648 |
+
except ValueError as e:
|
649 |
+
return {'display': 'none'}, f"Error: {str(e)}"
|
650 |
+
except Exception as e:
|
651 |
+
return {'display': 'none'}, f"Unexpected Error: {str(e)}"
|
652 |
+
|
653 |
+
|
654 |
+
|
655 |
+
@app.callback(
|
656 |
+
Output('credentials-section', 'style'),
|
657 |
+
[Input('modify-yaml-choice', 'value'),
|
658 |
+
Input('selected-model', 'value'),
|
659 |
+
Input('apply-button', 'n_clicks')]
|
660 |
+
|
661 |
+
)
|
662 |
+
def display_credentials(selection_update, selected_model, n_clicks):
|
663 |
+
"""
|
664 |
+
Toggles the display of the credentials input fields based on user input.
|
665 |
+
|
666 |
+
This function updates the CSS style of the credentials input fields to either
|
667 |
+
show or hide them based on the user's choice to update the YAML file and the
|
668 |
+
selection of a model from the dropdown.
|
669 |
+
|
670 |
+
Args:
|
671 |
+
selection_update (str): The user's choice to update the YAML file ('yes' or 'no').
|
672 |
+
selected_model (str): The selected model from the dropdown.
|
673 |
+
|
674 |
+
Returns:
|
675 |
+
dict: A dictionary containing the CSS style to either display or hide the credentials input fields.
|
676 |
+
"""
|
677 |
+
if n_clicks is None or n_clicks == 0:
|
678 |
+
return {'display': 'none'}
|
679 |
+
return {'display': 'block'}
|
680 |
+
|
681 |
+
|
682 |
+
@app.callback(
|
683 |
+
Output('process-button', 'style'),
|
684 |
+
[Input('apply-button', 'n_clicks')]
|
685 |
+
|
686 |
+
)
|
687 |
+
def display_launch_training(n_clicks):
|
688 |
+
"""
|
689 |
+
Displays the process button based on the number of clicks on the apply button.
|
690 |
+
|
691 |
+
Parameters:
|
692 |
+
n_clicks (int): The number of times the apply button has been clicked.
|
693 |
+
|
694 |
+
Returns:
|
695 |
+
dict: A dictionary containing the CSS style for the process button.
|
696 |
+
"""
|
697 |
+
if n_clicks and n_clicks > 0:
|
698 |
+
return {'display': 'inline-block'}
|
699 |
+
return {'display': 'none'}
|
700 |
+
|
701 |
+
@app.callback(
|
702 |
+
Output("results-section", "style"),
|
703 |
+
Input('process-button', 'n_clicks')
|
704 |
+
)
|
705 |
+
def display_results_section(n_clicks):
|
706 |
+
"""
|
707 |
+
Displays the results section based on the number of clicks on the process button.
|
708 |
+
|
709 |
+
Parameters:
|
710 |
+
n_clicks (int): The number of times the process button has been clicked.
|
711 |
+
|
712 |
+
Returns:
|
713 |
+
dict: A dictionary containing the CSS style for the results section.
|
714 |
+
"""
|
715 |
+
if n_clicks and n_clicks > 0:
|
716 |
+
return {"display": "block"}
|
717 |
+
else:
|
718 |
+
return {"display": "none"}
|
719 |
+
|
720 |
+
|
721 |
+
|
722 |
+
@app.callback(
|
723 |
+
[Output('log-reader', 'children'),
|
724 |
+
Output('acc-visualization', 'figure'),
|
725 |
+
Output('acc-visualization', 'style'),
|
726 |
+
Output('loss-visualization', 'figure'),
|
727 |
+
Output('loss-visualization', 'style')],
|
728 |
+
[Input('interval-widget', 'n_intervals'),
|
729 |
+
Input('process-button', 'n_clicks')],
|
730 |
+
[State('selected-model', 'value'),
|
731 |
+
State('devcloud-username-input', 'value'),
|
732 |
+
State('devcloud-password-input', 'value')]
|
733 |
+
)
|
734 |
+
def refresh_metrics(n_intervals, nb_clicks, selected_model, devcloud_username, devcloud_password):
|
735 |
+
"""
|
736 |
+
Updates the log display and training metrics based on user actions and intervals.
|
737 |
+
|
738 |
+
This function handles the following:
|
739 |
+
- Executes the training script when the run button is clicked and updates the logs.
|
740 |
+
- Periodically checks for new training metrics and updates the accuracy and loss graphs.
|
741 |
+
- Manages the display of the log and metrics components based on the training status.
|
742 |
+
|
743 |
+
Args:
|
744 |
+
n_intervals (int): The number of intervals that have passed for the interval component.
|
745 |
+
nb_clicks (int): The number of times the run button has been clicked.
|
746 |
+
selected_model (str): The selected model from the dropdown.
|
747 |
+
devcloud_username (str): The username for authentication.
|
748 |
+
devcloud_password (str): The password for authentication.
|
749 |
+
|
750 |
+
Returns:
|
751 |
+
tuple: A tuple containing:
|
752 |
+
- str: The updated log messages.
|
753 |
+
- dict: The figure data for the accuracy graph.
|
754 |
+
- dict: The CSS style to display or hide the accuracy graph.
|
755 |
+
- dict: The figure data for the loss graph.
|
756 |
+
- dict: The CSS style to display or hide the loss graph.
|
757 |
+
|
758 |
+
Raises:
|
759 |
+
PreventUpdate: If the callback context is not triggered by a relevant input.
|
760 |
+
"""
|
761 |
+
|
762 |
+
global logs, new_training
|
763 |
+
|
764 |
+
callback_context = dash.callback_context
|
765 |
+
if not callback_context.triggered:
|
766 |
+
raise PreventUpdate
|
767 |
+
|
768 |
+
button = callback_context.triggered[0]['prop_id'].split('.')[0]
|
769 |
+
|
770 |
+
if button == 'process-button' and nb_clicks:
|
771 |
+
if devcloud_username and devcloud_password:
|
772 |
+
st_script = f"stm32ai-modelzoo-services/{selected_model}/src/stm32ai_main.py"
|
773 |
+
execute_async(st_script, devcloud_username, devcloud_password)
|
774 |
+
new_training = True
|
775 |
+
logs.append("Starting application ...")
|
776 |
+
return "\n".join(logs), {}, {'display': 'none'}, {}, {'display': 'none'}
|
777 |
+
else:
|
778 |
+
logs.append("Please enter both ST Developer Cloud username and password:")
|
779 |
+
return "\n".join(logs), {}, {'display': 'none'}, {}, {'display': 'none'}
|
780 |
+
|
781 |
+
elif button == 'interval-widget':
|
782 |
+
if not new_training:
|
783 |
+
return "\n".join(logs), {}, {'display': 'none'}, {}, {'display': 'none'}
|
784 |
+
|
785 |
+
outputs_folder = "experiments_outputs"
|
786 |
+
|
787 |
+
if not os.path.exists(outputs_folder):
|
788 |
+
os.makedirs(outputs_folder)
|
789 |
+
return "\n".join(logs), {}, {'display': 'none'}, {}, {'display': 'none'}
|
790 |
+
|
791 |
+
dated_directories = [d for d in os.listdir(outputs_folder) if os.path.isdir(os.path.join(outputs_folder, d)) and d.startswith('20')]
|
792 |
+
if dated_directories:
|
793 |
+
recent_directory = max(dated_directories, key=lambda d: datetime.strptime(d, '%Y_%m_%d_%H_%M_%S'))
|
794 |
+
train_metrics_file = os.path.join(outputs_folder, recent_directory, 'logs', 'metrics', 'train_metrics.csv')
|
795 |
+
print(f"Metrics file : {train_metrics_file}")
|
796 |
+
if os.path.exists(train_metrics_file) and new_training:
|
797 |
+
metrics_dataframe = pd.read_csv(train_metrics_file)
|
798 |
+
if not metrics_dataframe.empty:
|
799 |
+
figures = []
|
800 |
+
metrics_pairs = [
|
801 |
+
('accuracy', 'val_accuracy'),
|
802 |
+
('loss', 'val_loss'),
|
803 |
+
('oks', 'val_oks'),
|
804 |
+
('val_map',)
|
805 |
+
]
|
806 |
+
|
807 |
+
for pair in metrics_pairs:
|
808 |
+
if len(pair) == 2:
|
809 |
+
train_metric, val_metric = pair
|
810 |
+
if train_metric in metrics_dataframe.columns and val_metric in metrics_dataframe.columns:
|
811 |
+
fig = {
|
812 |
+
'data': [
|
813 |
+
{
|
814 |
+
'x': metrics_dataframe['epoch'],
|
815 |
+
'y': metrics_dataframe[train_metric],
|
816 |
+
'type': 'line',
|
817 |
+
'name': train_metric.capitalize(),
|
818 |
+
'line': {'color': '#FFD200', 'width': 2, 'dash': 'solid'},
|
819 |
+
'hoverinfo': 'x+y+name',
|
820 |
+
'hoverlabel': {'bgcolor': '#EEEFF1', 'font': {'color': '#525A63'}}
|
821 |
+
},
|
822 |
+
{
|
823 |
+
'x': metrics_dataframe['epoch'],
|
824 |
+
'y': metrics_dataframe[val_metric],
|
825 |
+
'type': 'line',
|
826 |
+
'name': val_metric.capitalize(),
|
827 |
+
'line': {'color': '#3CB4E6', 'width': 2, 'dash': 'solid'},
|
828 |
+
'hoverinfo': 'x+y+name',
|
829 |
+
'hoverlabel': {'bgcolor': '#EEEFF1', 'font': {'color': '#525A63'}}
|
830 |
+
}
|
831 |
+
],
|
832 |
+
'layout': {
|
833 |
+
'xaxis': {
|
834 |
+
'title': 'Epochs',
|
835 |
+
'showgrid': True,
|
836 |
+
'gridcolor': '#EEEFF1',
|
837 |
+
'tickangle': 45
|
838 |
+
},
|
839 |
+
'yaxis': {
|
840 |
+
'title': train_metric.capitalize(),
|
841 |
+
'showgrid': True,
|
842 |
+
'gridcolor': '#EEEFF1'
|
843 |
+
},
|
844 |
+
'showlegend': True,
|
845 |
+
'legend': {
|
846 |
+
'x': 1,
|
847 |
+
'y': 1,
|
848 |
+
'traceorder': 'normal',
|
849 |
+
'font': {'size': 10},
|
850 |
+
'bgcolor': '#EEEFF1',
|
851 |
+
'bordercolor': '#A6ADB5',
|
852 |
+
'borderwidth': 1
|
853 |
+
},
|
854 |
+
'hovermode': 'closest',
|
855 |
+
'plot_bgcolor': '#ffffff'
|
856 |
+
}
|
857 |
+
}
|
858 |
+
figures.append(fig)
|
859 |
+
elif len(pair) == 1:
|
860 |
+
val_metric = pair[0]
|
861 |
+
if val_metric in metrics_dataframe.columns:
|
862 |
+
fig = {
|
863 |
+
'data': [
|
864 |
+
{
|
865 |
+
'x': metrics_dataframe['epoch'],
|
866 |
+
'y': metrics_dataframe[val_metric],
|
867 |
+
'type': 'line',
|
868 |
+
'name': val_metric.capitalize(),
|
869 |
+
'line': {'color': '#3CB4E6', 'width': 2, 'dash': 'solid'},
|
870 |
+
'hoverinfo': 'x+y+name',
|
871 |
+
'hoverlabel': {'bgcolor': '#EEEFF1', 'font': {'color': '#525A63'}}
|
872 |
+
}
|
873 |
+
],
|
874 |
+
'layout': {
|
875 |
+
'xaxis': {
|
876 |
+
'title': 'Epochs',
|
877 |
+
'showgrid': True,
|
878 |
+
'gridcolor': '#EEEFF1',
|
879 |
+
'tickangle': 45
|
880 |
+
},
|
881 |
+
'yaxis': {
|
882 |
+
'title': val_metric.capitalize(),
|
883 |
+
'showgrid': True,
|
884 |
+
'gridcolor': '#EEEFF1'
|
885 |
+
},
|
886 |
+
'showlegend': True,
|
887 |
+
'legend': {
|
888 |
+
'x': 1,
|
889 |
+
'y': 1,
|
890 |
+
'traceorder': 'normal',
|
891 |
+
'font': {'size': 10},
|
892 |
+
'bgcolor': '#EEEFF1',
|
893 |
+
'bordercolor': '#A6ADB5',
|
894 |
+
'borderwidth': 1
|
895 |
+
},
|
896 |
+
'hovermode': 'closest',
|
897 |
+
'plot_bgcolor': '#ffffff'
|
898 |
+
}
|
899 |
+
}
|
900 |
+
figures.append(fig)
|
901 |
+
|
902 |
+
if figures:
|
903 |
+
return "\n".join(logs), figures[0], {'display': 'block'}, figures[1] if len(figures) > 1 else {}, {'display': 'block'}
|
904 |
+
else:
|
905 |
+
return "\n".join(logs), {}, {'display': 'none'}, {}, {'display': 'none'}
|
906 |
+
else:
|
907 |
+
return "\n".join(logs), {}, {'display': 'none'}, {}, {'display': 'none'}
|
908 |
+
else:
|
909 |
+
return "\n".join(logs), {}, {'display': 'none'}, {}, {'display': 'none'}
|
910 |
+
else:
|
911 |
+
return "\n".join(logs), {}, {'display': 'none'}, {}, {'display': 'none'}
|
912 |
+
|
913 |
+
raise PreventUpdate
|
914 |
+
@app.callback(
|
915 |
+
Output('submission-outcome', 'children'),
|
916 |
+
[Input('apply-button', 'n_clicks'),
|
917 |
+
Input('process-button', 'n_clicks')],
|
918 |
+
[State({'type': 'yaml-setting', 'index': ALL}, 'id'),
|
919 |
+
State({'type': 'yaml-setting', 'index': ALL}, 'value'),
|
920 |
+
State('selected-model', 'value'),
|
921 |
+
State('devcloud-username-input', 'value'),
|
922 |
+
State('devcloud-password-input', 'value')]
|
923 |
+
)
|
924 |
+
def process_button_actions(submit_clicks, exec_nb_clicks, form_input_ids, form_input_values, selected_model, devcloud_username, devcloud_password):
|
925 |
+
"""
|
926 |
+
Handles the actions triggered by the submit and run buttons.
|
927 |
+
|
928 |
+
This function processes the form data when the submit button is clicked,
|
929 |
+
updates the corresponding YAML file, and executes the training script when
|
930 |
+
the run button is clicked.
|
931 |
+
|
932 |
+
Args:
|
933 |
+
submit_clicks (int): The number of times the submit button has been clicked.
|
934 |
+
exec_nb_clicks (int): The number of times the execution/run button has been clicked.
|
935 |
+
form_input_ids (list): A list of dictionaries containing the IDs of the form inputs.
|
936 |
+
form_input_values (list): A list of values from the form inputs.
|
937 |
+
selected_model (str): The selected model from the dropdown.
|
938 |
+
devcloud_username (str): The username for DevCloud authentication.
|
939 |
+
devcloud_password (str): The password for DevCloud authentication.
|
940 |
+
|
941 |
+
Returns:
|
942 |
+
str: A message indicating the result of the action, such as successful YAML update or script execution status.
|
943 |
+
|
944 |
+
Raises:
|
945 |
+
PreventUpdate: If the callback context is not triggered by a relevant input or if no action is taken.
|
946 |
+
"""
|
947 |
+
new_fields = []
|
948 |
+
|
949 |
+
callback_context = dash.callback_context
|
950 |
+
if not callback_context.triggered:
|
951 |
+
raise PreventUpdate
|
952 |
+
|
953 |
+
triggered_button = callback_context.triggered[0]['prop_id'].split('.')[0]
|
954 |
+
|
955 |
+
if triggered_button == 'apply-button':
|
956 |
+
if submit_clicks:
|
957 |
+
try:
|
958 |
+
form_fields_data = {}
|
959 |
+
for i in range(len(form_input_ids)):
|
960 |
+
input_id = form_input_ids[i]['index']
|
961 |
+
input_value = form_input_values[i]
|
962 |
+
form_fields_data[input_id] = input_value
|
963 |
+
|
964 |
+
yaml_file_path = local_yamls.get(selected_model)
|
965 |
+
if yaml_file_path :
|
966 |
+
yaml_parser = ruamel.yaml.YAML()
|
967 |
+
with open(yaml_file_path , 'r') as file:
|
968 |
+
current_yaml_data = yaml_parser.load(file)
|
969 |
+
|
970 |
+
updated_yaml_data = process_form_configs(form_fields_data)
|
971 |
+
for key, value in updated_yaml_data.items():
|
972 |
+
keys = key.split('.')
|
973 |
+
nested_dict = current_yaml_data
|
974 |
+
for k in keys[:-1]:
|
975 |
+
nested_dict = nested_dict.setdefault(k, {})
|
976 |
+
if nested_dict[keys[-1]] != value:
|
977 |
+
nested_dict[keys[-1]] = value
|
978 |
+
new_fields.append(key)
|
979 |
+
|
980 |
+
with open(yaml_file_path , 'w') as file:
|
981 |
+
yaml_parser.dump(current_yaml_data, file)
|
982 |
+
|
983 |
+
return f"User config yaml file has been updated successfully ! Updated fields are: {', '.join(new_fields)}"
|
984 |
+
else:
|
985 |
+
return f"ERROR: No user config yaml found for '{selected_model}'."
|
986 |
+
except Exception as e:
|
987 |
+
return f"ERROR: UPDATING USER CONFIG YAML file: {e}"
|
988 |
+
else:
|
989 |
+
raise PreventUpdate
|
990 |
+
elif triggered_button == 'process-button':
|
991 |
+
if exec_nb_clicks:
|
992 |
+
st_script = f"stm32ai-modelzoo-services/{selected_model}/src/stm32ai_main.py"
|
993 |
+
execute_async(st_script, devcloud_username, devcloud_password)
|
994 |
+
return "Application is running ..."
|
995 |
+
else:
|
996 |
+
raise PreventUpdate
|
997 |
+
|
998 |
+
|
999 |
+
|
1000 |
+
@app.callback(
|
1001 |
+
Output('download-action', 'style'),
|
1002 |
+
[Input('interval-widget', 'n_intervals')],
|
1003 |
+
[State('selected-model', 'value')]
|
1004 |
+
)
|
1005 |
+
def toggle_download_button(n_intervals, selected_model):
|
1006 |
+
"""
|
1007 |
+
Toggles the display of the download button based on the existence of output directories.
|
1008 |
+
|
1009 |
+
This function checks if the output directories for the selected model exist and
|
1010 |
+
toggles the display of the download button accordingly.
|
1011 |
+
|
1012 |
+
Args:
|
1013 |
+
n_intervals (int): The number of intervals that have passed for the interval component.
|
1014 |
+
model_choice (str): The selected model from the dropdown.
|
1015 |
+
|
1016 |
+
Returns:
|
1017 |
+
dict: A dictionary containing the CSS style to either display or hide the download button.
|
1018 |
+
"""
|
1019 |
+
out_directory = os.path.join(os.getcwd(), "experiments_outputs")
|
1020 |
+
|
1021 |
+
if not os.path.exists(out_directory ):
|
1022 |
+
return {'display': 'none'}
|
1023 |
+
|
1024 |
+
output_subdirectories = [d for d in os.listdir(out_directory ) if os.path.isdir(os.path.join(out_directory , d)) and d.startswith('20')]
|
1025 |
+
|
1026 |
+
if output_subdirectories:
|
1027 |
+
return {'display': 'block'}
|
1028 |
+
return {'display': 'none'}
|
1029 |
+
|
1030 |
+
|
1031 |
+
@app.callback(
|
1032 |
+
Output('download-resource', 'data'),
|
1033 |
+
[Input('download-action', 'n_clicks')],
|
1034 |
+
[State('selected-model', 'value')]
|
1035 |
+
)
|
1036 |
+
def generate_download_link(n_clicks, selected_model):
|
1037 |
+
"""
|
1038 |
+
Generates a download link based on the selected model and operation mode.
|
1039 |
+
|
1040 |
+
This function reads the YAML configuration for the selected model, determines the operation mode,
|
1041 |
+
and generates a download link for the appropriate file (ZIP or ELF/BIN) based on the operation mode.
|
1042 |
+
|
1043 |
+
Args:
|
1044 |
+
click_count (int): The number of times the download button has been clicked.
|
1045 |
+
selected_model (str): The selected model from the dropdown.
|
1046 |
+
|
1047 |
+
Returns:
|
1048 |
+
dcc.send_file: A Dash component to send the file for download.
|
1049 |
+
|
1050 |
+
Raises:
|
1051 |
+
PreventUpdate: If no relevant action is taken or the required files do not exist.
|
1052 |
+
"""
|
1053 |
+
|
1054 |
+
if n_clicks is None:
|
1055 |
+
raise PreventUpdate
|
1056 |
+
|
1057 |
+
|
1058 |
+
output_directory = os.path.join(os.getcwd(), "./experiments_outputs")
|
1059 |
+
|
1060 |
+
if not os.path.exists(output_directory ):
|
1061 |
+
raise PreventUpdate
|
1062 |
+
|
1063 |
+
|
1064 |
+
timestamped_directories = [d for d in os.listdir(output_directory ) if os.path.isdir(os.path.join(output_directory , d)) and d.startswith('20')]
|
1065 |
+
|
1066 |
+
timestamped_directories = [
|
1067 |
+
d for d in os.listdir(output_directory)
|
1068 |
+
if os.path.isdir(os.path.join(output_directory, d)) and d.startswith("20")
|
1069 |
+
]
|
1070 |
+
|
1071 |
+
if timestamped_directories:
|
1072 |
+
recent_directory = max(
|
1073 |
+
timestamped_directories,
|
1074 |
+
key=lambda d: datetime.strptime(d, "%Y_%m_%d_%H_%M_%S")
|
1075 |
+
)
|
1076 |
+
recent_directory_path = os.path.join(output_directory, recent_directory)
|
1077 |
+
zip_file_path = os.path.join(recent_directory_path, f"{recent_directory}.zip")
|
1078 |
+
|
1079 |
+
|
1080 |
+
if not os.path.exists(zip_file_path):
|
1081 |
+
create_archive(zip_file_path, recent_directory_path)
|
1082 |
+
|
1083 |
+
|
1084 |
+
if os.path.exists(zip_file_path):
|
1085 |
+
return dcc.send_file(zip_file_path)
|
1086 |
+
|
1087 |
+
raise PreventUpdate
|
1088 |
+
|
1089 |
+
@server.route('/download/<path:subpath>')
|
1090 |
+
def download_file(subpath):
|
1091 |
+
"""
|
1092 |
+
Route to download a file from the server.
|
1093 |
+
|
1094 |
+
Parameters:
|
1095 |
+
- subpath (str): The subpath of the file to be downloaded, relative to the './experiments_outputs' directory.
|
1096 |
+
|
1097 |
+
Returns:
|
1098 |
+
- Response: A Flask response object to send the file as an attachment if it exists.
|
1099 |
+
- tuple: A tuple containing an error message and a 404 status code if the file is not found.
|
1100 |
+
"""
|
1101 |
+
file_path = os.path.join(os.getcwd(), './experiments_outputs', subpath)
|
1102 |
+
if os.path.exists(file_path):
|
1103 |
+
return send_file(file_path, as_attachment=True)
|
1104 |
+
else:
|
1105 |
+
return "File not found", 404
|
1106 |
+
|
1107 |
+
|
1108 |
+
if __name__ == '__main__':
|
1109 |
+
app.run_server(host='0.0.0.0',port=7860, dev_tools_ui=True, dev_tools_hot_reload=True, threaded=True)
|
datasets/README.md
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# STM32 Model Zoo App
|
2 |
+
|
3 |
+
|
4 |
+
## Directory components:
|
5 |
+
|
6 |
+
This is a placeholder for datasets.
|
download_datasets.py
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# /*---------------------------------------------------------------------------------------------
|
2 |
+
# * Copyright (c) 2024 STMicroelectronics.
|
3 |
+
# * All rights reserved.
|
4 |
+
# *
|
5 |
+
# * This software is licensed under terms that can be found in the LICENSE file in
|
6 |
+
# * the root directory of this software component.
|
7 |
+
# * If no LICENSE file comes with this software, it is provided AS-IS.
|
8 |
+
# *--------------------------------------------------------------------------------------------*/
|
9 |
+
import os
|
10 |
+
import requests
|
11 |
+
import tarfile
|
12 |
+
import zipfile
|
13 |
+
|
14 |
+
def download_and_extract(url, dest_path, rename_to=None):
|
15 |
+
if not os.path.exists(dest_path):
|
16 |
+
os.makedirs(dest_path)
|
17 |
+
filename = url.split('/')[-1]
|
18 |
+
filepath = os.path.join(dest_path, filename)
|
19 |
+
|
20 |
+
try:
|
21 |
+
with requests.get(url, stream=True) as r:
|
22 |
+
r.raise_for_status()
|
23 |
+
with open(filepath, 'wb') as f:
|
24 |
+
for chunk in r.iter_content(chunk_size=8192):
|
25 |
+
f.write(chunk)
|
26 |
+
print(f"Downloaded {filename} successfully.")
|
27 |
+
|
28 |
+
extracted_folder = None
|
29 |
+
if filepath.endswith('.tar.gz') or filepath.endswith('.tgz'):
|
30 |
+
with tarfile.open(filepath, 'r:gz') as tar:
|
31 |
+
tar.extractall(path=dest_path)
|
32 |
+
extracted_folder = tar.getnames()[0].split('/')[0]
|
33 |
+
print(f"Extracted {filename} successfully.")
|
34 |
+
elif filepath.endswith('.zip'):
|
35 |
+
with zipfile.ZipFile(filepath, 'r') as zip_ref:
|
36 |
+
zip_ref.extractall(dest_path)
|
37 |
+
extracted_folder = zip_ref.namelist()[0].split('/')[0]
|
38 |
+
print(f"Extracted {filename} successfully.")
|
39 |
+
|
40 |
+
os.remove(filepath)
|
41 |
+
print(f"Removed {filename} successfully.")
|
42 |
+
|
43 |
+
if rename_to and extracted_folder:
|
44 |
+
extracted_folder_path = os.path.join(dest_path, extracted_folder)
|
45 |
+
new_folder = os.path.join(dest_path, rename_to)
|
46 |
+
if os.path.exists(extracted_folder_path):
|
47 |
+
os.rename(extracted_folder_path, new_folder)
|
48 |
+
print(f"Renamed {extracted_folder_path} to {new_folder} successfully.")
|
49 |
+
else:
|
50 |
+
print(f"Extracted folder {extracted_folder_path} does not exist.")
|
51 |
+
except Exception as e:
|
52 |
+
print(f"An error occurred: {e}")
|
53 |
+
|
54 |
+
datasets = {
|
55 |
+
'image_classification': 'http://download.tensorflow.org/example_images/flower_photos.tgz',
|
56 |
+
'human_activity_recognition': 'https://www.cis.fordham.edu/wisdm/includes/datasets/latest/WISDM_ar_latest.tar.gz',
|
57 |
+
'hand_posture': 'https://raw.githubusercontent.com/STMicroelectronics/stm32ai-modelzoo-services/main/hand_posture/datasets/ST_VL53L8CX_handposture_dataset.zip', # Updated URL
|
58 |
+
'audio_event_detection': 'https://github.com/karolpiczak/ESC-50/archive/master.zip'
|
59 |
+
}
|
60 |
+
|
61 |
+
for dataset, url in datasets.items():
|
62 |
+
print(f"Processing {dataset}...")
|
63 |
+
if dataset == 'audio_event_detection':
|
64 |
+
download_and_extract(url, f'/home/appuser/datasets/{dataset}', rename_to='ESC-50')
|
65 |
+
else:
|
66 |
+
download_and_extract(url, f'/home/appuser/datasets/{dataset}')
|
67 |
+
print(f"Finished processing {dataset}.")
|
models/README.md
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# STM32 Model Zoo App
|
2 |
+
|
3 |
+
|
4 |
+
## Directory components:
|
5 |
+
|
6 |
+
This is a placeholder for models.
|
requirements_dash.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
dash==2.17.0
|
2 |
+
dash-bootstrap-components==1.6.0
|
3 |
+
dash-html-components==2.0.0
|
4 |
+
dash-table==5.0.0
|
5 |
+
ruamel.yaml==0.18.6
|
6 |
+
Flask
|
7 |
+
flask-session
|
8 |
+
gunicorn==20.1.0
|
9 |
+
|
static/ST_logo_2024_blue.jpg
ADDED
templates/index.html
ADDED
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<style>
|
4 |
+
body {
|
5 |
+
padding-top: 30px;
|
6 |
+
font-family: 'Arial', sans-serif;
|
7 |
+
margin-left: 2em;
|
8 |
+
margin-bottom: 50px;
|
9 |
+
color: #03234b;
|
10 |
+
}
|
11 |
+
h1, h2, h3, p {
|
12 |
+
margin-top: 20px;
|
13 |
+
color: #03234b;
|
14 |
+
}
|
15 |
+
|
16 |
+
.table-of-contents {
|
17 |
+
margin-bottom: 30px;
|
18 |
+
margin-left: 2em;
|
19 |
+
font-family: 'Arial', sans-serif;
|
20 |
+
padding: 10px;
|
21 |
+
/*border: 1px solid #ddd;*/
|
22 |
+
width: 150px;
|
23 |
+
height: 200px
|
24 |
+
}
|
25 |
+
.table-of-contents ul {
|
26 |
+
list-style-type: none;
|
27 |
+
padding-left: 0;
|
28 |
+
}
|
29 |
+
.table-of-contents ul li {
|
30 |
+
margin: 5px 0;
|
31 |
+
}
|
32 |
+
|
33 |
+
|
34 |
+
.image-right {
|
35 |
+
width: 100px;
|
36 |
+
height: auto;
|
37 |
+
position: absolute;
|
38 |
+
top: 20px;
|
39 |
+
right: 20px;
|
40 |
+
}
|
41 |
+
|
42 |
+
.table {
|
43 |
+
margin-right: 20px;
|
44 |
+
border-collapse: collapse;
|
45 |
+
}
|
46 |
+
th, td {
|
47 |
+
border: 1px solid #000;
|
48 |
+
padding: 8px;
|
49 |
+
}
|
50 |
+
th {
|
51 |
+
background-color: #f2f2f2;
|
52 |
+
text-align: left;
|
53 |
+
}
|
54 |
+
tr:hover {
|
55 |
+
background-color: #f5f5f5;
|
56 |
+
}
|
57 |
+
a {
|
58 |
+
color: #1a73e8;
|
59 |
+
text-decoration: none;
|
60 |
+
}
|
61 |
+
a:hover {
|
62 |
+
text-decoration: underline;
|
63 |
+
}
|
64 |
+
|
65 |
+
|
66 |
+
</style>
|
67 |
+
</head>
|
68 |
+
|
69 |
+
<body>
|
70 |
+
<div class="container">
|
71 |
+
<h1 class="mt-5" style="color: #03234b">STMicroelectronics - STM32AI model zoo</h1>
|
72 |
+
<img src="{{url_for('static', filename='ST_logo_2024_blue.jpg')}}" alt="Logo" class="image-right">
|
73 |
+
|
74 |
+
<hr>
|
75 |
+
<p class="lead" style="color: #03234b">
|
76 |
+
Welcome to the STM32 Model zoo dockerized Dashboard ! <br>
|
77 |
+
This space is dedicated to run STM32 model zoo from the dashboard using Dash Plotly. This zoo is a collection of reference machine learning models that are optimized to run on STM32 microcontrollers. Available on GitHub, this is a valuable resource for anyone looking to add AI capabilities to their STM32-based projects.
|
78 |
+
</p>
|
79 |
+
|
80 |
+
<div class="container">
|
81 |
+
<div class="alert alert-block alert-info" style="background-color: #ceecf9; color: #03234b; margin-top: 20px">
|
82 |
+
<h2>Table of contents</h2>
|
83 |
+
<ul>
|
84 |
+
<li><a href="#Introduction">Introduction</a></li>
|
85 |
+
<li><a href="#UserGuide">User Guide</a></li>
|
86 |
+
<li><a href="UsageScenarios">Usage Scenarios</a></li>
|
87 |
+
<li><a href="#StartApplication">Start Application</a></li>
|
88 |
+
|
89 |
+
</ul>
|
90 |
+
</div>
|
91 |
+
<br>
|
92 |
+
<hr>
|
93 |
+
<h2 style="color: #03234b">Introduction</h2>
|
94 |
+
<p style="color: #03234b">The application provides a comprehensive dashboard interface for interacting with STM32 models and exploring their capabilities. It is developed using Flask and Dash, and is hosted on Hugging Face Spaces.<br>
|
95 |
+
This dashboard simplifies the use of the STM32 AI Model Zoo, enabling you to start training these models on Hugging Face with ease.
|
96 |
+
</p>
|
97 |
+
<div align="center" style="margin-top: 80px; padding: 20px 0;">
|
98 |
+
<p align="center">
|
99 |
+
<a href="https://www.python.org/downloads/" target="_blank"><img src="https://img.shields.io/badge/python-3.10-blue" /></a>
|
100 |
+
<a href="https://dash.plotly.com/installation" target="_blank"><img src="https://img.shields.io/badge/Dash%20Plotly-2.0.0-3CAEA3?style=flat&logo=plotly&logoColor=white&link=https://dash.plotly.com/installation"/></a>
|
101 |
+
<a href="https://stm32ai-cs.st.com/home" target="_blank"><img src="https://img.shields.io/badge/STM32Cube.AI-Developer%20Cloud-FFD700?style=flat&logo=stmicroelectronics&logoColor=white"/></a>
|
102 |
+
</p>
|
103 |
+
</div>
|
104 |
+
<h2>User Guide</h2>
|
105 |
+
<p>This application provides the necessary code to build a docker container that runs on Hugging Face Docker Space. This space hosts :</p>
|
106 |
+
<li style="color: #03234b" ><b>Dash application script:</b> This application provides a dashboard developed with Dash Plotly to run use cases from the STM32-Model Zoo. You can <b>Train</b>, <b>Evaluate</b> and <b>Benchmark</b> models seamlessly.</li>
|
107 |
+
<li style="color: #03234b" ><b>Dockerfile:</b> Sets up the desired environment and clones the repository from GitHub to ensure alignment with the latest updates. Model zoo is hosted <a href="https://github.com/STMicroelectronics/stm32ai-modelzoo-services" target="_blank">here</a>. You can find the licences information <a href="https://github.com/STMicroelectronics/stm32ai-modelzoo-services/blob/main/LICENSE.md" target="_blank">here</a>.</li>
|
108 |
+
<li style="color: #03234b" ><b>Datasets:</b> Downloads the dataset based on the use case. The user must provide the dataset link before building the docker image and launching the application. The table below summarizes the datasets handled by the script: <br>
|
109 |
+
<br>
|
110 |
+
<table class="centered-table">
|
111 |
+
<thead>
|
112 |
+
<tr>
|
113 |
+
<th>Dataset</th>
|
114 |
+
<th>License</th>
|
115 |
+
</tr>
|
116 |
+
</thead>
|
117 |
+
<tbody>
|
118 |
+
<tr>
|
119 |
+
<td><a href="http://download.tensorflow.org/example_images/flower_photos.tgz">Flower Photos</a></td>
|
120 |
+
<td><a href="https://creativecommons.org/licenses/by/2.0/">CC BY 2.0</a></td>
|
121 |
+
</tr>
|
122 |
+
<tr>
|
123 |
+
<td><a href="https://data.mendeley.com/datasets/tywbtsjrjv/1">Plant Village</a></td>
|
124 |
+
<td><a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0 1.0</a></td>
|
125 |
+
</tr>
|
126 |
+
<tr>
|
127 |
+
<td><a href="https://data.vision.ee.ethz.ch/cvl/datasets_extra/food-101/">Food-101</a></td>
|
128 |
+
<td><a href="https://github.com/STMicroelectronics/stm32ai-modelzoo/tree/main/image_classification/pretrained_models/mobilenetv2">-</a></td>
|
129 |
+
</tr>
|
130 |
+
<tr>
|
131 |
+
<td><a href="https://www.cis.fordham.edu/wisdm/includes/datasets/latest/WISDM_ar_latest.tar.gz">WISDM Activity Recognition</a></td>
|
132 |
+
<td><a href="https://creativecommons.org/licenses/by/2.0/">CC BY 2.0</a></td>
|
133 |
+
</tr>
|
134 |
+
<tr>
|
135 |
+
<td><a href="https://github.com/STMicroelectronics/stm32ai-modelzoo/raw/main/hand_posture/datasets/ST_VL53L8CX_handposture_dataset.zip">Hand Posture</a></td>
|
136 |
+
<td><a href="https://github.com/STMicroelectronics/stm32ai-modelzoo/blob/main/hand_posture/datasets/LICENSE.md">SLA008</a></td>
|
137 |
+
</tr>
|
138 |
+
<tr>
|
139 |
+
<td><a href="https://github.com/karolpiczak/ESC-50/archive/master.zip">ESC-50</a></td>
|
140 |
+
<td><a href="https://github.com/karolpiczak/ESC-50/blob/master/LICENSE">-</a></td>
|
141 |
+
</tr>
|
142 |
+
</tbody>
|
143 |
+
</table>
|
144 |
+
</li>
|
145 |
+
|
146 |
+
<h2>Usage Scenarios</h2>
|
147 |
+
<p style="color: #03234b">Before starting please make sure you have created an account in <a href="https://stm32ai-cs.st.com/home" target="_blank"> ST Edge AI Developer Cloud</a> <br>
|
148 |
+
To start using the features of this application follow these steps:
|
149 |
+
<div class="indented">
|
150 |
+
<li><strong>Go to the <em>Start Application</em> section</strong>: Duplicate this space to launch it on your Hugging Face space. This will automatically build the image and launch the container on your space.</li>
|
151 |
+
<p style= "margin-left: 2em">
|
152 |
+
<li> If you'd like to use one of these models, please follow these instructions:
|
153 |
+
<ul>
|
154 |
+
<li><em>Object Detection:</em> Please follow this <a href="https://github.com/STMicroelectronics/stm32ai-modelzoo-services/tree/main/object_detection/src#1" target="_blank">link</a> to perform dataset preparation.</li>
|
155 |
+
<br>
|
156 |
+
<li><em>Semantic Segmentation:</em> Please follow this <a href="https://github.com/STMicroelectronics/stm32ai-modelzoo-services/tree/main/semantic_segmentation/src#2-3" target="_blank">link</a> to get the dataset.</li>
|
157 |
+
<br>
|
158 |
+
<li><em>Pose Estimation:</em> Please follow this <a href="https://github.com/STMicroelectronics/stm32ai-modelzoo-services/tree/main/pose_estimation/src#1" target="_blank">link</a> to get the dataset.</li>
|
159 |
+
</ul>
|
160 |
+
</li>
|
161 |
+
|
162 |
+
<li><strong>Setup and launch your application:</strong> To launch one of the other use cases, first check the download_datasets.py script and make sure you have the link corresponding to your dataset to download. We provided links the script for datasets mentioned in the previous table.<br>
|
163 |
+
These datasets are downloaded automatically and do not require a data preparation phase as mentioned for Object Detection, Pose Segmentation, and Semantic Segmentation use cases.</li>
|
164 |
+
<br>
|
165 |
+
<strong>How to use the dashboard ? </strong><br>
|
166 |
+
<ol>
|
167 |
+
<li>Select the use case from the drop-down list.</li>
|
168 |
+
<br>
|
169 |
+
<li>A corresponding YAML configuration file will open, and you'll need to configure it to launch a training session.<br>
|
170 |
+
If you're using a use case with one of the datasets handled by the download_datasets.py script, go to the Dataset section in the YAML file and configure the path to your data accordingly : <em>../datasets/your_use_case/name_of_dataset</em><br>
|
171 |
+
<br>
|
172 |
+
For example, if you selected Image classification use case with flowers dataset the path should be this way: <em>../datasets/image_classification/flowers_photo</em> <br>
|
173 |
+
<br>
|
174 |
+
For the other use cases that need data preparation, we recommend you place your dataset in the datasets folder and set your path accordingly: <em>datasets/name_of_your_dataset</em>
|
175 |
+
</li>
|
176 |
+
<br>
|
177 |
+
<li>Submit your modifications and insert your ST Edge AI Developer cloud to benefit from the whole experience.<br>
|
178 |
+
Your training session will start, allowing you to visualize output logs and metrics plots. Finally, you can download your experiment outputs.<br>
|
179 |
+
</li>
|
180 |
+
</ol>
|
181 |
+
|
182 |
+
<p> If you need to explore, evaluate, and benchmark pre-trained models, you can use <a href="https://github.com/STMicroelectronics/stm32ai-modelzoo" target="_blank">stm32ai-modelzoo</a></p>
|
183 |
+
<br>
|
184 |
+
|
185 |
+
<em> Note: </em>
|
186 |
+
<li>Deployment on edge is not supported.</li>
|
187 |
+
<li>If you need to reproduce the same experience provided in the model zoo, please refer to <a href="https://github.com/STMicroelectronics/stm32ai-modelzoo" target="_blank">stm32ai-modelzoo</a> </li>
|
188 |
+
<li>If you want to discover different services offered by the model zoo, please refer to <a href="https://github.com/STMicroelectronics/stm32ai-modelzoo-services" target="_blank">stm32ai-modelzoo-services</a> </li></li>
|
189 |
+
<li> If you need to use your own model, please add it to the models folder, rebuild the Docker image, and then update the model path in the user configuration YAML file.</li>
|
190 |
+
</p>
|
191 |
+
|
192 |
+
|
193 |
+
<h2>Start Application</h2>
|
194 |
+
<p style="color: #03234b">Once the space has been duplicated from the home page, you need to update the script <em>donwnlod_datasets.py</em> script that will allow you to download and unarchive the dataset corresponding to your use case.
|
195 |
+
<br>
|
196 |
+
{% if duplicate_mode %}
|
197 |
+
<h2>Duplicate this Space</h2>
|
198 |
+
<p style="color: #03234b"> > To start using this application, duplicate this space to your own Hugging Face account.</p>
|
199 |
+
<a href="https://huggingface.co/spaces/STMicroelectronics/stm32-modelzoo-app?duplicate=true" target="_blank" style="background-color: #03234b; color: white; padding: 10px 20px; text-decoration: none;
|
200 |
+
border-radius: 5px; border: 2px solid #03234b; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);">Duplicate this Space</a>
|
201 |
+
{% else %}
|
202 |
+
<p>You have duplicated the space. Now you can launch the interactive dashboard below.</p>
|
203 |
+
<a href="/dash_app/" target="_blank" style="background-color: #03234b; color: white; padding: 10px 20px; text-decoration: none;
|
204 |
+
border-radius: 5px; border: 2px solid #03234b; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);">Launch Dashboard</a>
|
205 |
+
{% endif %}
|
206 |
+
|
207 |
+
<div class="spacer"></div>
|
208 |
+
<hr>
|
209 |
+
</div>
|
210 |
+
</body>
|
211 |
+
</html>
|