Improved GUI.
Browse files- README.md +19 -1
- app.py +214 -33
- requirements.txt +3 -3
README.md
CHANGED
@@ -11,6 +11,24 @@ license: cc-by-4.0
|
|
11 |
short_description: Fake ECG Generator
|
12 |
---
|
13 |
|
|
|
|
|
14 |
Allows to generate ECGs. Based on the following paper:
|
15 |
|
16 |
-
https://www.nature.com/articles/s41598-021-01295-2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
short_description: Fake ECG Generator
|
12 |
---
|
13 |
|
14 |
+
# Deepfake ECG Generator GUI
|
15 |
+
|
16 |
Allows to generate ECGs. Based on the following paper:
|
17 |
|
18 |
+
https://www.nature.com/articles/s41598-021-01295-2
|
19 |
+
|
20 |
+
# Run locally
|
21 |
+
|
22 |
+
## Prepare venv and install dependencies
|
23 |
+
```bash
|
24 |
+
mkdir -p ~/python-environments/deepfake-ecg
|
25 |
+
python3 -m venv ~/python-environments/deepfake-ecg
|
26 |
+
. ~/python-environments/deepfake-ecg/bin/activate
|
27 |
+
pip install -r requirements.txt
|
28 |
+
```
|
29 |
+
|
30 |
+
## Run the application
|
31 |
+
```bash
|
32 |
+
./app.py
|
33 |
+
```
|
34 |
+
Then, connect a web browser to [http://127.0.0.1:7860/](http://127.0.0.1:7860/) to use the application
|
app.py
CHANGED
@@ -1,36 +1,217 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
#
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
import ecg_plot
|
|
|
|
|
6 |
import matplotlib.pyplot as plt
|
7 |
-
|
|
|
8 |
import torch
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python
|
2 |
+
# -*- coding: utf-8 -*-
|
3 |
+
# ==========================================================================
|
4 |
+
# ____ __ _ _____ ____ ____
|
5 |
+
# | _ \ ___ ___ _ __ / _| __ _| | _____ | ____/ ___/ ___|
|
6 |
+
# | | | |/ _ \/ _ \ '_ \| |_ / _` | |/ / _ \ | _|| | | | _
|
7 |
+
# | |_| | __/ __/ |_) | _| (_| | < __/ | |__| |__| |_| |
|
8 |
+
# |____/ \___|\___| .__/|_| \__,_|_|\_\___| |_____\____\____|
|
9 |
+
# |_|
|
10 |
+
#
|
11 |
+
# --- Deepfake ECG Generator ---
|
12 |
+
# https://github.com/vlbthambawita/deepfake-ecg
|
13 |
+
# ==========================================================================
|
14 |
+
#
|
15 |
+
# DeepfakeECG GUI Application
|
16 |
+
# Copyright (C) 2023-2025 by Vajira Thambawita
|
17 |
+
# Copyright (C) 2025 by Thomas Dreibholz
|
18 |
+
#
|
19 |
+
# This program is free software: you can redistribute it and/or modify
|
20 |
+
# it under the terms of the GNU General Public License as published by
|
21 |
+
# the Free Software Foundation, either version 3 of the License, or
|
22 |
+
# (at your option) any later version.
|
23 |
+
|
24 |
+
# This program is distributed in the hope that it will be useful,
|
25 |
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
26 |
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
27 |
+
# GNU General Public License for more details.
|
28 |
+
#
|
29 |
+
# You should have received a copy of the GNU General Public License
|
30 |
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
31 |
+
#
|
32 |
+
# Contact:
|
33 |
+
# * Vajira Thambawita <[email protected]>
|
34 |
+
# * Thomas Dreibholz <[email protected]>
|
35 |
+
|
36 |
+
import deepfakeecg
|
37 |
import ecg_plot
|
38 |
+
import io
|
39 |
+
import gradio
|
40 |
import matplotlib.pyplot as plt
|
41 |
+
import matplotlib.ticker
|
42 |
+
import transformers
|
43 |
import torch
|
44 |
+
import typing
|
45 |
+
import PIL
|
46 |
+
|
47 |
+
|
48 |
+
# ###### Generate ECGs ######################################################
|
49 |
+
def predict(numberOfECGs = 1, ecgLengthInSeconds = 10, ecgTypeString = 'ECG-12') -> list:
|
50 |
+
|
51 |
+
# ====== Set ECG type ====================================================
|
52 |
+
ecgType = deepfakeecg.DATA_ECG12
|
53 |
+
if ecgTypeString == 'ECG-8':
|
54 |
+
ecgType = deepfakeecg.DATA_ECG8
|
55 |
+
elif ecgTypeString == 'ECG-12':
|
56 |
+
ecgType = deepfakeecg.DATA_ECG12
|
57 |
+
else:
|
58 |
+
sys.stderr.write(f'WARNING: Invalid ecgTypeString {ecgTypeString}, using ECG-12!\n')
|
59 |
+
|
60 |
+
# ====== Raise Locator.MAXTICKS, if necessary ============================
|
61 |
+
matplotlib.ticker.Locator.MAXTICKS = \
|
62 |
+
max(1000, ecgLengthInSeconds * deepfakeecg.ECG_SAMPLING_RATE)
|
63 |
+
# print(matplotlib.ticker.Locator.MAXTICKS)
|
64 |
+
|
65 |
+
# ====== Generate the ECGs ===============================================
|
66 |
+
results = deepfakeecg.generateDeepfakeECGs(numberOfECGs,
|
67 |
+
ecgType = ecgType,
|
68 |
+
ecgLengthInSeconds = ecgLengthInSeconds,
|
69 |
+
ecgScaleFactor = 6,
|
70 |
+
outputFormat = deepfakeecg.OUTPUT_TENSOR,
|
71 |
+
showProgress = False,
|
72 |
+
runOnDevice = runOnDevice)
|
73 |
+
|
74 |
+
# ====== Create a list of image/label tuples for gradio.Gallery ==========
|
75 |
+
plotList = []
|
76 |
+
ecgNumber = 1
|
77 |
+
for result in results:
|
78 |
+
|
79 |
+
# ====== Plot ECG =====================================================
|
80 |
+
result = result.t().detach().cpu().numpy()
|
81 |
+
# print(result)
|
82 |
+
|
83 |
+
# ------ ECG-12 -------------------------------------------------------
|
84 |
+
if ecgType == deepfakeecg.DATA_ECG12:
|
85 |
+
ecg_plot.plot(result,
|
86 |
+
title = 'ECG-12',
|
87 |
+
sample_rate = deepfakeecg.ECG_SAMPLING_RATE,
|
88 |
+
lead_index = [ 'I', 'II', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'III', 'aVR', 'aVL', 'aVF' ],
|
89 |
+
lead_order = [0, 1, 8, 9, 10, 11, 2, 3, 4, 5, 6, 7],
|
90 |
+
show_grid = True)
|
91 |
+
# ------ ECG-8 --------------------------------------------------------
|
92 |
+
else:
|
93 |
+
ecg_plot.plot(result,
|
94 |
+
title = 'ECG-8',
|
95 |
+
sample_rate = deepfakeecg.ECG_SAMPLING_RATE,
|
96 |
+
lead_index = [ 'I', 'II', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6' ],
|
97 |
+
lead_order = [0, 1, 2, 3, 4, 5, 6, 7],
|
98 |
+
show_grid = True)
|
99 |
+
|
100 |
+
# ====== Generate WebP output =========================================
|
101 |
+
imageBuffer = io.BytesIO()
|
102 |
+
plt.savefig(imageBuffer, format = 'webp')
|
103 |
+
plt.close()
|
104 |
+
image = PIL.Image.open(imageBuffer)
|
105 |
+
plotList.append( (image, f'ECG Number {ecgNumber}') )
|
106 |
+
|
107 |
+
ecgNumber = ecgNumber + 1
|
108 |
+
|
109 |
+
return plotList
|
110 |
+
|
111 |
+
|
112 |
+
|
113 |
+
# ###### Main program #######################################################
|
114 |
+
|
115 |
+
# ====== Initialise =========================================================
|
116 |
+
runOnDevice: typing.Literal['cpu', 'cuda'] = 'cuda' if torch.cuda.is_available() else 'cpu'
|
117 |
+
css = r"""
|
118 |
+
div {
|
119 |
+
background-image: url("https://www.nntb.no/~dreibh/graphics/backgrounds/background-essen.png");
|
120 |
+
}
|
121 |
+
|
122 |
+
/* ###### General Settings ############################################## */
|
123 |
+
html, body {
|
124 |
+
height: 100%;
|
125 |
+
padding: 0;
|
126 |
+
margin: 0;
|
127 |
+
font-family: sans-serif;
|
128 |
+
font-size: small;
|
129 |
+
background-color: #E3E3E3; /* Simula background colour: #E3E3E3 */
|
130 |
+
}
|
131 |
+
|
132 |
+
|
133 |
+
/* ###### Header ######################################################## */
|
134 |
+
div.header {
|
135 |
+
background-image: none;
|
136 |
+
background-color: #F15D22; /* Simula header colour: #F15D22 */
|
137 |
+
height: 7.5%;
|
138 |
+
display: flex;
|
139 |
+
justify-content: space-between;
|
140 |
+
}
|
141 |
+
|
142 |
+
div.logo-left {
|
143 |
+
width: 12.5%;
|
144 |
+
float: left;
|
145 |
+
display: flex;
|
146 |
+
padding: 0% 1%;
|
147 |
+
align-items: center;
|
148 |
+
background: white;
|
149 |
+
}
|
150 |
+
|
151 |
+
div.logo-right {
|
152 |
+
width: 12.5%;
|
153 |
+
float: right;
|
154 |
+
display: flex;
|
155 |
+
padding: 0% 1%;
|
156 |
+
align-items: center;
|
157 |
+
background: white;
|
158 |
+
}
|
159 |
+
|
160 |
+
div.title {
|
161 |
+
display: flex;
|
162 |
+
align-items: center;
|
163 |
+
padding: 0% 1%;
|
164 |
+
background-image: none;
|
165 |
+
background-color: #F15D22; /* Simula header colour: #F15D22 */
|
166 |
+
|
167 |
+
font-family: "Ubuntu", sans-serif;
|
168 |
+
font-size: 4vh;
|
169 |
+
font-weight: bold;
|
170 |
+
}r
|
171 |
+
|
172 |
+
img.logo-image {
|
173 |
+
max-width: 100%;
|
174 |
+
max-height: 100%;
|
175 |
+
}
|
176 |
+
"""
|
177 |
+
|
178 |
+
# ====== Create GUI =========================================================
|
179 |
+
with gradio.Blocks(css = css, theme = gradio.themes.Glass(secondary_hue=gradio.themes.colors.blue)) as gui:
|
180 |
+
big_block = gradio.HTML("""
|
181 |
+
<div class="header">
|
182 |
+
<div class="logo-left">
|
183 |
+
<img class="logo-image" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iNDUyLjk3N3B4IiBoZWlnaHQ9IjEyNC43MjVweCIgdmlld0JveD0iMCAwIDQ1Mi45NzcgMTI0LjcyNSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNDUyLjk3NyAxMjQuNzI1Ig0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxnPg0KCQk8cGF0aCBmaWxsPSIjRjE1QTIyIiBkPSJNNDMzLjg1NCw5Mi42OTJjMCwxMS4yNi03LjQ0OCwxNi42MjktMTguMTg4LDE2LjYyOWMtOS43LDAtMTQuMjA0LTQuNTA0LTE0LjIwNC0xMS4yNTkNCgkJCWMwLTYuNTgyLDQuMTU3LTkuMDA3LDEzLjUxMi0xMC45MTNsNy42MjEtMS41NThjNS44ODktMS4yMTMsOC4zMTMtMS45MDYsMTEuMjU5LTIuOTQ1VjkyLjY5MnogTTQ1Mi43MzQsMTAzLjA4NlY2Mi4yMDgNCgkJCWMwLTE4LjE4OS0xMy44NTctMjQuMjUtMzIuNzM3LTI0LjI1Yy0xOS43NDYsMC0zNi4zNzUsOC4xNDEtMzYuMDI3LDI4LjQwOGwxOC41MzIsMS4yMTJjLTAuMTczLTkuNzAxLDUuNTQzLTE0LjM3NiwxNi42MjktMTQuMzc2DQoJCQljMTAuMjIsMCwxNC43MjQsMy40NjQsMTQuNzI0LDEwLjM5MnYxLjM4NmMwLDMuMTE4LTAuODY2LDMuNDY0LTUuMTk2LDQuMzNsLTE4LjE4OCwzLjExOGMtMTAuNzM5LDEuOTA2LTE5LjQsNS4xOTctMjQuNDI0LDExLjI1OQ0KCQkJYy0yLjk0MywzLjYzOS00LjY3Nyw4LjMxNC00LjY3NywxNC41NWMwLDE4LjAxNSwxMy4xNjUsMjUuOTgyLDI5LjEwMSwyNS45ODJjMTQuMTg4LDAsMTkuNzkzLTcuMDA4LDIzLjQxLTEwLjU3OXY4LjA2MmgxOC44NDQNCgkJCXYtMTguNTA2QzQ1Mi43MjcsMTAzLjE2LDQ1Mi43MzQsMTAzLjEyMSw0NTIuNzM0LDEwMy4wODYgTTU5Ljk3NCw2Mi43NzdjLTUuNzE3LTUuNTQzLTEzLjY4NC05LjM1My0yMy4yMTEtOS4zNTMNCgkJCWMtNy43OTUsMC0xMy41MTEsMi43NzEtMTMuNTExLDguMTQxYzAsNS44OSw1LjcxNiw3LjEwMiwxNy4zMjIsOS41MjZsOS44NzMsMi4yNTJjMTIuNDcxLDIuOTQ1LDIzLjczLDcuNjIyLDIzLjczLDIyLjY5MQ0KCQkJYzAsMTguODgtMTcuMTQ4LDI3Ljg4Ny0zNi4wMjgsMjcuODg3Yy0xOS43NDcsMC0zMy4wODQtOS42OTktMzguMTA3LTE2LjgwMWwxMy4zMzctMTEuNzc5DQoJCQljNC42NzcsNi4yMzYsMTIuNjQ1LDEyLjI5OCwyNS4yODksMTIuMjk4YzkuODczLDAsMTUuNTg5LTMuNDY0LDE1LjU4OS05LjM1M2MwLTUuNzE3LTQuNjc2LTYuNzU1LTEyLjY0NC04LjQ4OGwtMTIuMjk4LTIuNTk4DQoJCQljLTE0LjM3Ny0zLjExOC0yNS42MzYtOS4xODEtMjUuNjM2LTI0LjI1YzAtMTguMDE1LDE3LjQ5NS0yNS42MzYsMzMuMjU3LTI1LjYzNmMxNi40NTUsMCwyOS4xLDcuMjc1LDM0LjQ3LDEzLjE2NEw1OS45NzQsNjIuNzc3eg0KCQkJIE0xODcsNDUuMjg0Yy00Ljg1LTUuMzctMTEuOTUyLTcuOTY5LTIwLjQzOS03Ljk2OWMtMTIuOTkxLDAtMjEuODI1LDUuNzE3LTI1LjgwOSwxMi42NDVWMzkuMzkzaC0xOS43NDZ2ODIuNDUxaDE5Ljc0NlY3Ny42NzUNCgkJCWMwLTEzLjMzOCw1LjcxNi0yMi44NjYsMTcuODQxLTIyLjg2NmMxNC44OTYsMCwxNS45MzYsMTAuMzk0LDE1LjkzNiwyNS4yOTF2NDEuNzQ0aDE5Ljc0NlY3Ny42NzUNCgkJCWMwLTEzLjMzOCw1LjU0My0yMi44NjYsMTcuODQxLTIyLjg2NmMxNC44OTcsMCwxNS45MzgsMTAuMzk0LDE1LjkzOCwyNS4yOTF2NDEuNzQ0SDI0Ny44Vjc1LjA3Ng0KCQkJYzAtMTguMTg4LTIuMDc4LTIzLjkwNC03LjI3NC0yOS43OTJjLTQuMzMxLTQuODUxLTExLjI1OS03Ljk2OS0yMS44MjctNy45NjljLTE0Ljg5NiwwLTIzLjIxMSw2Ljc1NS0yOC4yMzQsMTIuODE4DQoJCQlDMTg5LjQyNSw0OC40LDE4OC4zODYsNDYuODQyLDE4Nyw0NS4yODQgTTI2MS44NTMsMzkuMzkzaDE5Ljc0NnY0My44MjRjMCw1Ljg5LDAuMTc0LDExLjYwNiwyLjA3OCwxNS45MzYNCgkJCWMxLjkwNiw0LjMzLDUuNzE3LDcuMjc1LDEyLjY0Niw3LjI3NWMxMi4yOTgsMCwxNy42NjgtOS41MjcsMTcuNjY4LTIyLjg2NHYtNDQuMTdoMTkuNzQ2djgyLjQ1MUgzMTMuOTl2LTEwLjU2Ng0KCQkJYy0zLjk4NCw2LjkyOS0xMi44MTcsMTIuNjQ1LTI0LjU5NywxMi42NDVjLTguMzE0LDAtMTUuNDE2LTIuNTk4LTIwLjI2Ni03Ljk2N2MtNS4xOTctNS44OTEtNy4yNzUtMTEuNjA2LTcuMjc1LTI5Ljc5M1YzOS4zOTN6DQoJCQkgTTEwNS4zNTUsMzkuMzgySDg1LjU5MXY4Mi40NjFoMTkuNzY0VjM5LjM4MnogTTM2OS4wNjksMTIxLjg0NGgtMTkuNzQ2VjQuMDU4aDE5Ljc0NlYxMjEuODQ0eiBNODIuMTIyLDEzLjk4Nw0KCQkJYzAsNy4zOTQsNi4wMjQsMTMuNjA5LDEzLjM1MSwxMy42MDljNy40OSwwLDEzLjM1Mi02LjIxNiwxMy4zNTItMTMuNjA5YzAtNy43MjktNS44NjItMTMuOTQ0LTEzLjM1Mi0xMy45NDQNCgkJCUM4OC4xNDYsMC4wNDIsODIuMTIyLDYuMjU4LDgyLjEyMiwxMy45ODciLz4NCgk8L2c+DQo8L2c+DQo8L3N2Zz4NCg==" alt="SimulaMet" height="32" />
|
184 |
+
</div>
|
185 |
+
<div class="title" id="title"><a href="https://ihi-search.eu/">SEARCH</a> Fake ECG Generator</div>
|
186 |
+
<div class="logo-right">
|
187 |
+
<img class="logo-image" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMTAzNy44OTVweCIgaGVpZ2h0PSIxNzQuMjVweCIgdmlld0JveD0iMCAwIDEwMzcuODk1IDE3NC4yNSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTAzNy44OTUgMTc0LjI1Ig0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxwYXRoIGZpbGw9IiNGMTVBMjIiIGQ9Ik0xMTUuMTAzLDIwLjkzN2MwLDEwLjIyNiw4LjM0MywxOC44NCwxOC40ODEsMTguODRjMTAuMzcxLDAsMTguNDcyLTguNjE0LDE4LjQ3Mi0xOC44NA0KCQljMC0xMC42OTEtOC4xMDEtMTkuMjk2LTE4LjQ3Mi0xOS4yOTZDMTIzLjQ0NSwxLjY0MSwxMTUuMTAzLDEwLjI0NSwxMTUuMTAzLDIwLjkzNyBNNTEyLjI1NiwxNzAuMjIyaC0yNy4zMzdWNy4xOTloMjcuMzM3VjE3MC4yMjINCgkJeiBNMTQ3LjI1Miw1Ni4wNzVoLTI3LjM1OHYxMTQuMTI3aDI3LjM1OFY1Ni4wNzV6IE0zNjMuODY2LDU2LjA5NWgyNy4zMzh2NjAuNjcyYzAsOC4xMywwLjIzMywxNi4wNDYsMi44NzEsMjIuMDQyDQoJCWMyLjYzOSw1Ljk3Niw3LjkxNiwxMC4wNjgsMTcuNSwxMC4wNjhjMTcuMDE3LDAsMjQuNDQ4LTEzLjE5MiwyNC40NDgtMzEuNjQ1VjU2LjA5NWgyNy4zMzl2MTE0LjEyN2gtMjcuMzM5di0xNC42NDkNCgkJYy01LjUwOSw5LjYwNS0xNy43NTIsMTcuNTIxLTM0LjAzMiwxNy41MjFjLTExLjUwNiwwLTIxLjM0My0zLjYwOC0yOC4wNTctMTEuMDQxYy03LjE5Ny04LjE0Ny0xMC4wNjgtMTYuMDQ1LTEwLjA2OC00MS4yM1Y1Ni4wOTV6DQoJCSBNMjU5LjU0NSw2NC4yNDNjLTYuNzEzLTcuNDMxLTE2LjU1LTExLjAyMS0yOC4yODktMTEuMDIxYy0xNy45ODYsMC0zMC4xOTEsNy44OTYtMzUuNzIsMTcuNTAydi0xNC42M2gtMjcuMzE5djExNC4xMjdoMjcuMzE5DQoJCXYtNjEuMTM5YzAtMTguNDcyLDcuOTE2LTMxLjY0NiwyNC42OTktMzEuNjQ2YzIwLjYyNSwwLDIyLjA2MSwxNC4zNzcsMjIuMDYxLDM1LjAwMnY1Ny43ODJoMjcuMzM4di02MS4xMzkNCgkJYzAtMTguNDcyLDcuNjY1LTMxLjY0NiwyNC42ODEtMzEuNjQ2YzIwLjYwNSwwLDIyLjA2MSwxNC4zNzcsMjIuMDYxLDM1LjAwMnY1Ny43ODJoMjcuMzE5di02NC43MjkNCgkJYzAtMjUuMTY1LTIuODUzLTMzLjEwMS0xMC4wNTEtNDEuMjVjLTUuOTk2LTYuNzEzLTE1LjYtMTEuMDIxLTMwLjIyOS0xMS4wMjFjLTIwLjYwNiwwLTMyLjExMiw5LjM1Mi0zOS4wNzcsMTcuNzMzDQoJCUMyNjIuOTAyLDY4LjU1MiwyNjEuNDg1LDY2LjQxNiwyNTkuNTQ1LDY0LjI0MyBNODQuNDQ2LDg4LjQ1OEM3Ni41NCw4MC43OTQsNjUuNTEsNzUuNTE3LDUyLjMxNSw3NS41MTcNCgkJYy0xMC43ODgsMC0xOC42OTQsMy44MjItMTguNjk0LDExLjI1NGMwLDguMTY4LDcuOTA2LDkuODM3LDIzLjk4MSwxMy4xOTNsMTMuNjUsMy4xMjRjMTcuMjY5LDQuMDc0LDMyLjg2OCwxMC41NTUsMzIuODY4LDMxLjQxMg0KCQljMCwyNi4xMzYtMjMuNzQ5LDM4LjU5My00OS44NzQsMzguNTkzYy0yNy4zMzksMC00NS44LTEzLjQyNy01Mi43NDYtMjMuMjQ0bDE4LjQ1MS0xNi4zMTgNCgkJYzYuNDgxLDguNjM2LDE3LjUwMiwxNy4wMTcsMzUuMDEzLDE3LjAxN2MxMy42NjksMCwyMS41NzUtNC43NzMsMjEuNTc1LTEyLjkyNGMwLTcuOTM1LTYuNDgtOS4zNy0xNy41MDEtMTEuNzU3bC0xNy4wMzUtMy42MDgNCgkJQzIyLjExNiwxMTcuOTUsNi41NDQsMTA5LjU2OCw2LjU0NCw4OC43MWMwLTI0LjkzMywyNC4xOTUtMzUuNDg3LDQ2LjAxNC0zNS40ODdjMjIuNzg4LDAsNDAuMjcsMTAuMDY5LDQ3LjcyMSwxOC4yMkw4NC40NDYsODguNDU4DQoJCXogTTYyOC4wNywxNDQuMjZWODcuNjYyYzAtMjUuMTY1LTE5LjIwOS0zMy41NDctNDUuMzI0LTMzLjU0N2MtMjcuMzM5LDAtNTAuMzUsMTEuMjUzLTQ5Ljg2NSwzOS4zMTFsMjUuNjUsMS42NjgNCgkJYy0wLjI1My0xMy40MjcsNy42NjUtMTkuODg4LDIzLjAxMi0xOS44ODhjMTQuMTYzLDAsMjAuMzczLDQuNzkyLDIwLjM3MywxNC4zOTZ2MS45MDJjMCw0LjMwNy0xLjE4Myw0Ljc5Mi03LjE5OCw1Ljk5NA0KCQlsLTI1LjE2Niw0LjMyN2MtMTQuODYxLDIuNjQtMjYuODM0LDcuMTgtMzMuOCwxNS41NjJjLTQuMDczLDUuMDQ1LTYuNDc5LDExLjUyNC02LjQ3OSwyMC4xNTkNCgkJYzAsMjQuOTMzLDE4LjIxOSwzNS45NTMsNDAuMjc4LDM1Ljk1M2MxOS42NTUsMCwyNy4zOTYtOS43MDEsMzIuNDAzLTE0LjY0OHYxMS4xNzZoMjYuMDk4di0yNS42MzENCgkJQzYyOC4wNTMsMTQ0LjM1Nyw2MjguMDcsMTQ0LjMsNjI4LjA3LDE0NC4yNiBNNjAxLjkxNiwxMjkuODYzYzAsMTUuNTgtMTAuMjg0LDIzLjAxMi0yNS4xNjYsMjMuMDEyDQoJCWMtMTMuNDI3LDAtMTkuNjUzLTYuMjI5LTE5LjY1My0xNS41ODFjMC05LjExOCw1Ljc2Mi0xMi40NzYsMTguNzA0LTE1LjA5NWwxMC41MzMtMi4xNTRjOC4xNS0xLjY2OCwxMS41MDctMi42NTcsMTUuNTgyLTQuMDczDQoJCVYxMjkuODYzeiIvPg0KCTxwYXRoIGZpbGw9IiMyNDEzNjMiIGQ9Ik0xMDM2LjUyNSwxNDkuMzgxYy0yLjg4NCwwLjQ4LTcuMjA5LDAuOTYxLTEyLjAxNiwwLjk2MWMtNC44MDYsMC05LjM3MS0wLjcyMS0xMS41MzQtMy4xMjQNCgkJYy0yLjE2My0yLjE2Mi0zLjEyNC01LjI4Ni0zLjEyNC0xMS43NzRWNzcuMDUyaDI2LjY3NFY1NS42NjZoLTI2LjY3NFY5Ljc3bC0yNy4zOTMsNi4wMDd2MzkuODloLTI0LjUxdjIxLjM4NmgyNC41MXY2Mi45NTgNCgkJYzAsMTEuMDUzLDAuMjQsMTcuNTQxLDQuODA1LDIzLjA2N2M2LjI0OCw3LjY4OSwxNy43ODIsOS44NTMsMjguODM2LDkuODUzYzguMTcsMCwxNS4xMzktMC43MjEsMjAuNDI2LTEuOTIyVjE0OS4zODF6DQoJCSBNODk5LjMxNiw3NC44OWMxNS4zNzksMCwyNi4xOTEsMTAuMDkyLDI2LjkxMiwyNC45OWgtNTUuNzQ5Qzg3MS4yMDEsODYuMTgzLDg4My42OTcsNzQuODksODk5LjMxNiw3NC44OSBNOTM0Ljg3OSwxMjkuNjc3DQoJCWMtMy42MDQsNS43NjctMTIuNzM1LDE5LjcwNC0zMy40MDEsMTkuNzA0Yy0yMC4xODQsMC0yOS43OTYtMTMuNDU2LTMxLjcxOS0yOS4wNzZoODMuMzgzYzAtMS45MjIsMC4yNC01LjA0NiwwLjI0LTYuOTY4DQoJCWMwLTM1LjA4My0xNy41NDEtNjAuMzE0LTU0LjA2NS02MC4zMTRjLTMxLjk2MSwwLTU2LjIyOSwyMy41NDktNTYuMjI5LDU4Ljg3MmMwLDM3LjAwNSwyMy4wNjgsNjEuMDM1LDU2Ljk1LDYxLjAzNQ0KCQljMzMuNCwwLDQ4Ljc3OS0yMC45MDYsNTMuODI2LTI4LjgzNUw5MzQuODc5LDEyOS42Nzd6IE03NDAuOTYyLDYzLjgzNmMtNi43MjgtNy40NDktMTYuNTgtMTEuMDU1LTI4LjM1NS0xMS4wNTUNCgkJYy0xOC4wMjEsMC0zMC4yNzYsNy45MzEtMzUuODA0LDE3LjU0MlY1NS42NjZINjQ5LjQxdjExNC4zOGgyNy4zOTN2LTYxLjI3NWMwLTE4LjUwMyw3LjkzLTMxLjcxOSwyNC43NTEtMzEuNzE5DQoJCWMyMC42NjUsMCwyMi4xMDcsMTQuNDE4LDIyLjEwNywzNS4wODN2NTcuOTExaDI3LjM5M3YtNjEuMjc1YzAtMTguNTAzLDcuNjktMzEuNzE5LDI0Ljc1MS0zMS43MTkNCgkJYzIwLjY2NSwwLDIyLjEwNywxNC40MTgsMjIuMTA3LDM1LjA4M3Y1Ny45MTFoMjcuMzk0di02NC44NzljMC0yNS4yMzEtMi44ODQtMzMuMTYxLTEwLjA5My00MS4zMzENCgkJYy02LjAwNy02LjcyOS0xNS42MTktMTEuMDU1LTMwLjI3Ny0xMS4wNTVjLTIwLjY2NSwwLTMyLjE5OSw5LjM3Mi0zOS4xNjgsMTcuNzgyQzc0NC4zMjYsNjguMTYsNzQyLjg4NCw2NS45OTksNzQwLjk2Miw2My44MzYiLz4NCjwvZz4NCjwvc3ZnPg0K" alt="NorNet" height="64" />
|
188 |
+
</div>
|
189 |
+
</div>
|
190 |
+
""")
|
191 |
+
gradio.Markdown('## Settings')
|
192 |
+
with gradio.Row():
|
193 |
+
sliderNumberOfECGs = gradio.Slider(1, 100, label="Number of ECGs", step = 1, value = 4, interactive = True)
|
194 |
+
sliderLengthInSeconds = gradio.Slider(5, 60, label="Length (s)", step = 5, value = 10, interactive = True)
|
195 |
+
dropdownType = gradio.Dropdown( [ 'ECG-12', 'ECG-8' ], label = 'ECG Type', interactive = True)
|
196 |
+
buttonGenerate = gradio.Button("Generate")
|
197 |
+
gradio.Markdown('## Output')
|
198 |
+
with gradio.Row():
|
199 |
+
outputGallery = gradio.Gallery(label = 'output', columns = [ 1 ], height = 'auto',
|
200 |
+
show_label = True,
|
201 |
+
preview = True)
|
202 |
+
|
203 |
+
# ====== Add click event handling for "Generate" button ==================
|
204 |
+
buttonGenerate.click(predict,
|
205 |
+
inputs = [ sliderNumberOfECGs, sliderLengthInSeconds, dropdownType ],
|
206 |
+
outputs = [ outputGallery ]
|
207 |
+
)
|
208 |
+
|
209 |
+
# ====== Run on startup ==================================================
|
210 |
+
gui.load(predict,
|
211 |
+
inputs = [ sliderNumberOfECGs, sliderLengthInSeconds, dropdownType ],
|
212 |
+
outputs = [ outputGallery ]
|
213 |
+
)
|
214 |
+
|
215 |
+
# ====== Run the GUI ========================================================
|
216 |
+
if __name__ == "__main__":
|
217 |
+
gui.launch()
|
requirements.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
transformers
|
2 |
-
torch
|
3 |
ecg-plot
|
|
|
4 |
matplotlib
|
5 |
Pillow
|
6 |
-
pydantic==2.10.6
|
|
|
|
|
|
|
|
1 |
ecg-plot
|
2 |
+
gradio
|
3 |
matplotlib
|
4 |
Pillow
|
5 |
+
pydantic==2.10.6
|
6 |
+
torch
|