dreibh commited on
Commit
a72a986
·
verified ·
1 Parent(s): 3cd4799

Improved GUI.

Browse files
Files changed (3) hide show
  1. README.md +19 -1
  2. app.py +214 -33
  3. 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
- import io
2
- import gradio as gr
3
- #from transformers import pipeline
4
- from transformers import AutoModel
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  import ecg_plot
 
 
6
  import matplotlib.pyplot as plt
7
- from PIL import Image
 
8
  import torch
9
- #pipeline = pipeline(task="image-classification", model="julien-c/hotdog-not-hotdog")
10
- model = AutoModel.from_pretrained("deepsynthbody/deepfake_ecg", trust_remote_code=True)
11
-
12
- def predict():
13
- prediction = (model(1)[0].t()/1000) # to micro volte
14
-
15
-
16
- lead_III = (prediction[1] - prediction[0]).unsqueeze(dim=0)
17
- lead_aVR = ((prediction[0] + prediction[1])*(-0.5)).unsqueeze(dim=0)
18
- lead_aVL = (prediction[0] - prediction[1]* 0.5).unsqueeze(dim=0)
19
- lead_aVF = (prediction[1] - prediction[0]* 0.5).unsqueeze(dim=0)
20
- all = torch.cat((prediction, lead_III, lead_aVR, lead_aVL, lead_aVF), dim=0)
21
- all_corrected = all[torch.tensor([0,1,8, 9, 10, 11, 2,3,4,5,6,7])]
22
-
23
- ecg_plot.plot(all_corrected, sample_rate = 500, title = 'ECG 12')
24
-
25
- #ecg_plot.show()
26
- buf = io.BytesIO()
27
- plt.savefig(buf, format="png")
28
- img = Image.open(buf)
29
- return img
30
-
31
- gr.Interface(
32
- predict,
33
- inputs=None,
34
- outputs="image",
35
- title="Generating Fake ECGs",
36
- ).launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>&nbsp;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