|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import datetime |
|
import deepfakeecg |
|
import ecg_plot |
|
import gradio |
|
import io |
|
import matplotlib.pyplot as plt |
|
import matplotlib.ticker |
|
import numpy |
|
import pathlib |
|
import random |
|
import sys |
|
import tempfile |
|
import threading |
|
import torch |
|
import typing |
|
import PIL |
|
|
|
|
|
TempDirectory = None |
|
Sessions = {} |
|
|
|
|
|
|
|
def log(logstring): |
|
print(('\x1b[34m' + datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S') + |
|
': ' + logstring + '\x1b[0m')); |
|
|
|
|
|
|
|
class Session: |
|
|
|
|
|
def __init__(self): |
|
self.Lock = threading.Lock() |
|
self.Counter = 0 |
|
self.Selected = 0 |
|
self.Results = None |
|
self.Type = None |
|
self.TempDirectory = tempfile.TemporaryDirectory(dir = TempDirectory.name) |
|
log(f'Prepared temporary directory {self.TempDirectory.name}') |
|
|
|
|
|
def __del__(self): |
|
log(f'Cleaning up temporary directory {self.TempDirectory.name}') |
|
self.TempDirectory.cleanup() |
|
|
|
|
|
def increment(self): |
|
with self.lock: |
|
self.counter += 1 |
|
return self.counter |
|
|
|
|
|
def increment(self): |
|
with self.lock: |
|
self.counter += 1 |
|
return self.counter |
|
|
|
|
|
|
|
def initializeSession(request: gradio.Request): |
|
Sessions[request.session_hash] = Session() |
|
log(f'Session "{request.session_hash}" initialized') |
|
|
|
|
|
|
|
def cleanUpSession(request: gradio.Request): |
|
if request.session_hash in Sessions: |
|
del instances[request.session_hash] |
|
log(f'Session "{request.session_hash}" cleaned up') |
|
|
|
|
|
|
|
def incrementCounter(request: gradio.Request): |
|
if request.session_hash in Sessions: |
|
instance = Sessions[request.session_hash] |
|
return instance.increment() |
|
log(f'ERROR: Session "{request.session_hash}" is not initialized!') |
|
|
|
|
|
|
|
|
|
def predict(numberOfECGs: int = 1, |
|
|
|
ecgTypeString: str = 'ECG-12', |
|
generatorModel: str = 'Default', |
|
request: gradio.Request = None) -> list: |
|
|
|
ecgLengthInSeconds = 10 |
|
|
|
log(f'Session "{request.session_hash}": Generate EGCs!') |
|
|
|
|
|
|
|
ecgType = deepfakeecg.DATA_ECG12 |
|
if ecgTypeString == 'ECG-8': |
|
ecgType = deepfakeecg.DATA_ECG8 |
|
elif ecgTypeString == 'ECG-12': |
|
ecgType = deepfakeecg.DATA_ECG12 |
|
else: |
|
sys.stderr.write(f'WARNING: Invalid ecgTypeString {ecgTypeString}, using ECG-12!\n') |
|
|
|
|
|
matplotlib.ticker.Locator.MAXTICKS = \ |
|
max(1000, ecgLengthInSeconds * deepfakeecg.ECG_SAMPLING_RATE) |
|
|
|
|
|
|
|
Sessions[request.session_hash].Results = \ |
|
deepfakeecg.generateDeepfakeECGs(numberOfECGs, |
|
ecgType = ecgType, |
|
ecgLengthInSeconds = ecgLengthInSeconds, |
|
ecgScaleFactor = deepfakeecg.ECG_DEFAULT_SCALE_FACTOR, |
|
outputFormat = deepfakeecg.OUTPUT_TENSOR, |
|
showProgress = False, |
|
runOnDevice = runOnDevice) |
|
Sessions[request.session_hash].Type = ecgType |
|
|
|
|
|
plotList = [] |
|
ecgNumber = 1 |
|
info = '25 mm/sec, 1 mV/10 mm' |
|
for result in Sessions[request.session_hash].Results: |
|
|
|
|
|
|
|
|
|
|
|
result = result.t().detach().cpu().numpy()[1:] / 1000 |
|
|
|
|
|
|
|
if ecgType == deepfakeecg.DATA_ECG12: |
|
ecg_plot.plot(result, |
|
title = 'ECG-12 – ' + info, |
|
sample_rate = deepfakeecg.ECG_SAMPLING_RATE, |
|
lead_index = [ 'I', 'II', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'III', 'aVR', 'aVL', 'aVF' ], |
|
lead_order = [0, 1, 8, 9, 10, 11, 2, 3, 4, 5, 6, 7], |
|
show_grid = True) |
|
|
|
else: |
|
ecg_plot.plot(result, |
|
title = 'ECG-8 – ' + info, |
|
sample_rate = deepfakeecg.ECG_SAMPLING_RATE, |
|
lead_index = [ 'I', 'II', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6' ], |
|
lead_order = [0, 1, 2, 3, 4, 5, 6, 7], |
|
show_grid = True) |
|
|
|
|
|
imageBuffer = io.BytesIO() |
|
plt.savefig(imageBuffer, format = 'webp') |
|
plt.close() |
|
image = PIL.Image.open(imageBuffer) |
|
plotList.append( (image, f'ECG Number {ecgNumber}') ) |
|
|
|
ecgNumber = ecgNumber + 1 |
|
|
|
return plotList |
|
|
|
|
|
|
|
def select(event: gradio.SelectData, |
|
request: gradio.Request): |
|
|
|
|
|
|
|
Sessions[request.session_hash].Selected = event.index |
|
log(f'Session "{request.session_hash}": Selected ECG #{Sessions[request.session_hash].Selected + 1}') |
|
|
|
|
|
|
|
def downloadCSV(request: gradio.Request) -> pathlib.Path: |
|
|
|
ecgResult = Sessions[request.session_hash].Results[Sessions[request.session_hash].Selected] |
|
ecgType = Sessions[request.session_hash].Type |
|
fileName = pathlib.Path(Sessions[request.session_hash].TempDirectory.name) / \ |
|
('ECG-' + str(Sessions[request.session_hash].Selected + 1) + '.csv') |
|
deepfakeecg.dataToCSV(ecgResult, ecgType, fileName) |
|
|
|
log(f'Session "{request.session_hash}": Download CSV file {fileName}') |
|
return fileName |
|
|
|
|
|
|
|
|
|
def downloadPDF(request: gradio.Request): |
|
|
|
ecgResult = Sessions[request.session_hash].Results[Sessions[request.session_hash].Selected] |
|
ecgType = Sessions[request.session_hash].Type |
|
fileName = pathlib.Path(Sessions[request.session_hash].TempDirectory.name) / \ |
|
('ECG-' + str(Sessions[request.session_hash].Selected + 1) + '.pdf') |
|
if ecgType == deepfakeecg.DATA_ECG12: |
|
outputLeads = [ 'I', 'II', 'III', 'aVL', 'aVR', 'aVF', 'V1', 'V2', 'V3', 'V4' , 'V5' , 'V6' ] |
|
else: |
|
outputLeads = [ 'I', 'II', 'V1', 'V2', 'V3', 'V4' , 'V5' , 'V6' ] |
|
|
|
deepfakeecg.dataToPDF(ecgResult, ecgType, outputLeads, fileName, |
|
Sessions[request.session_hash].Selected + 1) |
|
|
|
log(f'Session "{request.session_hash}": Download PDF file {fileName}') |
|
return fileName |
|
|
|
|
|
|
|
def analyze(request: gradio.Request): |
|
|
|
log(f'Session "{request.session_hash}": Analyze ECG #{Sessions[request.session_hash].Selected + 1}!') |
|
|
|
data = Sessions[request.session_hash].Results[Sessions[request.session_hash].Selected] |
|
print(data) |
|
|
|
|
|
|
|
|
|
|
|
|
|
runOnDevice: typing.Literal['cpu', 'cuda'] = 'cuda' if torch.cuda.is_available() else 'cpu' |
|
css = r""" |
|
div { |
|
background-image: url("https://www.nntb.no/~dreibh/graphics/backgrounds/background-essen.png"); |
|
} |
|
|
|
/* ###### General Settings ############################################## */ |
|
html, body { |
|
height: 100%; |
|
padding: 0; |
|
margin: 0; |
|
font-family: sans-serif; |
|
font-size: small; |
|
background-color: #E3E3E3; /* Simula background colour: #E3E3E3 */ |
|
} |
|
|
|
|
|
/* ###### Header ######################################################## */ |
|
div.header { |
|
background-image: none; |
|
background-color: #F15D22; /* Simula header colour: #F15D22 */ |
|
height: 7.5%; |
|
display: flex; |
|
justify-content: space-between; |
|
} |
|
|
|
div.logo-left { |
|
width: 12.5%; |
|
float: left; |
|
display: flex; |
|
padding: 0% 1%; |
|
align-items: center; |
|
background: white; |
|
} |
|
|
|
div.logo-right { |
|
width: 12.5%; |
|
float: right; |
|
display: flex; |
|
padding: 0% 1%; |
|
align-items: center; |
|
background: white; |
|
} |
|
|
|
div.title { |
|
display: flex; |
|
align-items: center; |
|
padding: 0% 1%; |
|
background-image: none; |
|
background-color: #F15D22; /* Simula header colour: #F15D22 */ |
|
|
|
font-family: "Ubuntu", sans-serif; |
|
font-size: 4vh; |
|
font-weight: bold; |
|
}r |
|
|
|
img.logo-image { |
|
max-width: 100%; |
|
max-height: 100%; |
|
} |
|
""" |
|
|
|
|
|
|
|
with gradio.Blocks(css = css, theme = gradio.themes.Glass(secondary_hue=gradio.themes.colors.blue)) as gui: |
|
|
|
|
|
|
|
gui.load(initializeSession) |
|
|
|
gui.unload(cleanUpSession) |
|
|
|
|
|
big_block = gradio.HTML(""" |
|
<div class="header"> |
|
<div class="logo-left"> |
|
<img class="logo-image" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iNDUyLjk3N3B4IiBoZWlnaHQ9IjEyNC43MjVweCIgdmlld0JveD0iMCAwIDQ1Mi45NzcgMTI0LjcyNSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgNDUyLjk3NyAxMjQuNzI1Ig0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxnPg0KCQk8cGF0aCBmaWxsPSIjRjE1QTIyIiBkPSJNNDMzLjg1NCw5Mi42OTJjMCwxMS4yNi03LjQ0OCwxNi42MjktMTguMTg4LDE2LjYyOWMtOS43LDAtMTQuMjA0LTQuNTA0LTE0LjIwNC0xMS4yNTkNCgkJCWMwLTYuNTgyLDQuMTU3LTkuMDA3LDEzLjUxMi0xMC45MTNsNy42MjEtMS41NThjNS44ODktMS4yMTMsOC4zMTMtMS45MDYsMTEuMjU5LTIuOTQ1VjkyLjY5MnogTTQ1Mi43MzQsMTAzLjA4NlY2Mi4yMDgNCgkJCWMwLTE4LjE4OS0xMy44NTctMjQuMjUtMzIuNzM3LTI0LjI1Yy0xOS43NDYsMC0zNi4zNzUsOC4xNDEtMzYuMDI3LDI4LjQwOGwxOC41MzIsMS4yMTJjLTAuMTczLTkuNzAxLDUuNTQzLTE0LjM3NiwxNi42MjktMTQuMzc2DQoJCQljMTAuMjIsMCwxNC43MjQsMy40NjQsMTQuNzI0LDEwLjM5MnYxLjM4NmMwLDMuMTE4LTAuODY2LDMuNDY0LTUuMTk2LDQuMzNsLTE4LjE4OCwzLjExOGMtMTAuNzM5LDEuOTA2LTE5LjQsNS4xOTctMjQuNDI0LDExLjI1OQ0KCQkJYy0yLjk0MywzLjYzOS00LjY3Nyw4LjMxNC00LjY3NywxNC41NWMwLDE4LjAxNSwxMy4xNjUsMjUuOTgyLDI5LjEwMSwyNS45ODJjMTQuMTg4LDAsMTkuNzkzLTcuMDA4LDIzLjQxLTEwLjU3OXY4LjA2MmgxOC44NDQNCgkJCXYtMTguNTA2QzQ1Mi43MjcsMTAzLjE2LDQ1Mi43MzQsMTAzLjEyMSw0NTIuNzM0LDEwMy4wODYgTTU5Ljk3NCw2Mi43NzdjLTUuNzE3LTUuNTQzLTEzLjY4NC05LjM1My0yMy4yMTEtOS4zNTMNCgkJCWMtNy43OTUsMC0xMy41MTEsMi43NzEtMTMuNTExLDguMTQxYzAsNS44OSw1LjcxNiw3LjEwMiwxNy4zMjIsOS41MjZsOS44NzMsMi4yNTJjMTIuNDcxLDIuOTQ1LDIzLjczLDcuNjIyLDIzLjczLDIyLjY5MQ0KCQkJYzAsMTguODgtMTcuMTQ4LDI3Ljg4Ny0zNi4wMjgsMjcuODg3Yy0xOS43NDcsMC0zMy4wODQtOS42OTktMzguMTA3LTE2LjgwMWwxMy4zMzctMTEuNzc5DQoJCQljNC42NzcsNi4yMzYsMTIuNjQ1LDEyLjI5OCwyNS4yODksMTIuMjk4YzkuODczLDAsMTUuNTg5LTMuNDY0LDE1LjU4OS05LjM1M2MwLTUuNzE3LTQuNjc2LTYuNzU1LTEyLjY0NC04LjQ4OGwtMTIuMjk4LTIuNTk4DQoJCQljLTE0LjM3Ny0zLjExOC0yNS42MzYtOS4xODEtMjUuNjM2LTI0LjI1YzAtMTguMDE1LDE3LjQ5NS0yNS42MzYsMzMuMjU3LTI1LjYzNmMxNi40NTUsMCwyOS4xLDcuMjc1LDM0LjQ3LDEzLjE2NEw1OS45NzQsNjIuNzc3eg0KCQkJIE0xODcsNDUuMjg0Yy00Ljg1LTUuMzctMTEuOTUyLTcuOTY5LTIwLjQzOS03Ljk2OWMtMTIuOTkxLDAtMjEuODI1LDUuNzE3LTI1LjgwOSwxMi42NDVWMzkuMzkzaC0xOS43NDZ2ODIuNDUxaDE5Ljc0NlY3Ny42NzUNCgkJCWMwLTEzLjMzOCw1LjcxNi0yMi44NjYsMTcuODQxLTIyLjg2NmMxNC44OTYsMCwxNS45MzYsMTAuMzk0LDE1LjkzNiwyNS4yOTF2NDEuNzQ0aDE5Ljc0NlY3Ny42NzUNCgkJCWMwLTEzLjMzOCw1LjU0My0yMi44NjYsMTcuODQxLTIyLjg2NmMxNC44OTcsMCwxNS45MzgsMTAuMzk0LDE1LjkzOCwyNS4yOTF2NDEuNzQ0SDI0Ny44Vjc1LjA3Ng0KCQkJYzAtMTguMTg4LTIuMDc4LTIzLjkwNC03LjI3NC0yOS43OTJjLTQuMzMxLTQuODUxLTExLjI1OS03Ljk2OS0yMS44MjctNy45NjljLTE0Ljg5NiwwLTIzLjIxMSw2Ljc1NS0yOC4yMzQsMTIuODE4DQoJCQlDMTg5LjQyNSw0OC40LDE4OC4zODYsNDYuODQyLDE4Nyw0NS4yODQgTTI2MS44NTMsMzkuMzkzaDE5Ljc0NnY0My44MjRjMCw1Ljg5LDAuMTc0LDExLjYwNiwyLjA3OCwxNS45MzYNCgkJCWMxLjkwNiw0LjMzLDUuNzE3LDcuMjc1LDEyLjY0Niw3LjI3NWMxMi4yOTgsMCwxNy42NjgtOS41MjcsMTcuNjY4LTIyLjg2NHYtNDQuMTdoMTkuNzQ2djgyLjQ1MUgzMTMuOTl2LTEwLjU2Ng0KCQkJYy0zLjk4NCw2LjkyOS0xMi44MTcsMTIuNjQ1LTI0LjU5NywxMi42NDVjLTguMzE0LDAtMTUuNDE2LTIuNTk4LTIwLjI2Ni03Ljk2N2MtNS4xOTctNS44OTEtNy4yNzUtMTEuNjA2LTcuMjc1LTI5Ljc5M1YzOS4zOTN6DQoJCQkgTTEwNS4zNTUsMzkuMzgySDg1LjU5MXY4Mi40NjFoMTkuNzY0VjM5LjM4MnogTTM2OS4wNjksMTIxLjg0NGgtMTkuNzQ2VjQuMDU4aDE5Ljc0NlYxMjEuODQ0eiBNODIuMTIyLDEzLjk4Nw0KCQkJYzAsNy4zOTQsNi4wMjQsMTMuNjA5LDEzLjM1MSwxMy42MDljNy40OSwwLDEzLjM1Mi02LjIxNiwxMy4zNTItMTMuNjA5YzAtNy43MjktNS44NjItMTMuOTQ0LTEzLjM1Mi0xMy45NDQNCgkJCUM4OC4xNDYsMC4wNDIsODIuMTIyLDYuMjU4LDgyLjEyMiwxMy45ODciLz4NCgk8L2c+DQo8L2c+DQo8L3N2Zz4NCg==" alt="SimulaMet" height="32" /> |
|
</div> |
|
<div class="title" id="title"><a href="https://ihi-search.eu/">SEARCH</a> Fake ECG Generator</div> |
|
<div class="logo-right"> |
|
<img class="logo-image" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMTAzNy44OTVweCIgaGVpZ2h0PSIxNzQuMjVweCIgdmlld0JveD0iMCAwIDEwMzcuODk1IDE3NC4yNSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTAzNy44OTUgMTc0LjI1Ig0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxwYXRoIGZpbGw9IiNGMTVBMjIiIGQ9Ik0xMTUuMTAzLDIwLjkzN2MwLDEwLjIyNiw4LjM0MywxOC44NCwxOC40ODEsMTguODRjMTAuMzcxLDAsMTguNDcyLTguNjE0LDE4LjQ3Mi0xOC44NA0KCQljMC0xMC42OTEtOC4xMDEtMTkuMjk2LTE4LjQ3Mi0xOS4yOTZDMTIzLjQ0NSwxLjY0MSwxMTUuMTAzLDEwLjI0NSwxMTUuMTAzLDIwLjkzNyBNNTEyLjI1NiwxNzAuMjIyaC0yNy4zMzdWNy4xOTloMjcuMzM3VjE3MC4yMjINCgkJeiBNMTQ3LjI1Miw1Ni4wNzVoLTI3LjM1OHYxMTQuMTI3aDI3LjM1OFY1Ni4wNzV6IE0zNjMuODY2LDU2LjA5NWgyNy4zMzh2NjAuNjcyYzAsOC4xMywwLjIzMywxNi4wNDYsMi44NzEsMjIuMDQyDQoJCWMyLjYzOSw1Ljk3Niw3LjkxNiwxMC4wNjgsMTcuNSwxMC4wNjhjMTcuMDE3LDAsMjQuNDQ4LTEzLjE5MiwyNC40NDgtMzEuNjQ1VjU2LjA5NWgyNy4zMzl2MTE0LjEyN2gtMjcuMzM5di0xNC42NDkNCgkJYy01LjUwOSw5LjYwNS0xNy43NTIsMTcuNTIxLTM0LjAzMiwxNy41MjFjLTExLjUwNiwwLTIxLjM0My0zLjYwOC0yOC4wNTctMTEuMDQxYy03LjE5Ny04LjE0Ny0xMC4wNjgtMTYuMDQ1LTEwLjA2OC00MS4yM1Y1Ni4wOTV6DQoJCSBNMjU5LjU0NSw2NC4yNDNjLTYuNzEzLTcuNDMxLTE2LjU1LTExLjAyMS0yOC4yODktMTEuMDIxYy0xNy45ODYsMC0zMC4xOTEsNy44OTYtMzUuNzIsMTcuNTAydi0xNC42M2gtMjcuMzE5djExNC4xMjdoMjcuMzE5DQoJCXYtNjEuMTM5YzAtMTguNDcyLDcuOTE2LTMxLjY0NiwyNC42OTktMzEuNjQ2YzIwLjYyNSwwLDIyLjA2MSwxNC4zNzcsMjIuMDYxLDM1LjAwMnY1Ny43ODJoMjcuMzM4di02MS4xMzkNCgkJYzAtMTguNDcyLDcuNjY1LTMxLjY0NiwyNC42ODEtMzEuNjQ2YzIwLjYwNSwwLDIyLjA2MSwxNC4zNzcsMjIuMDYxLDM1LjAwMnY1Ny43ODJoMjcuMzE5di02NC43MjkNCgkJYzAtMjUuMTY1LTIuODUzLTMzLjEwMS0xMC4wNTEtNDEuMjVjLTUuOTk2LTYuNzEzLTE1LjYtMTEuMDIxLTMwLjIyOS0xMS4wMjFjLTIwLjYwNiwwLTMyLjExMiw5LjM1Mi0zOS4wNzcsMTcuNzMzDQoJCUMyNjIuOTAyLDY4LjU1MiwyNjEuNDg1LDY2LjQxNiwyNTkuNTQ1LDY0LjI0MyBNODQuNDQ2LDg4LjQ1OEM3Ni41NCw4MC43OTQsNjUuNTEsNzUuNTE3LDUyLjMxNSw3NS41MTcNCgkJYy0xMC43ODgsMC0xOC42OTQsMy44MjItMTguNjk0LDExLjI1NGMwLDguMTY4LDcuOTA2LDkuODM3LDIzLjk4MSwxMy4xOTNsMTMuNjUsMy4xMjRjMTcuMjY5LDQuMDc0LDMyLjg2OCwxMC41NTUsMzIuODY4LDMxLjQxMg0KCQljMCwyNi4xMzYtMjMuNzQ5LDM4LjU5My00OS44NzQsMzguNTkzYy0yNy4zMzksMC00NS44LTEzLjQyNy01Mi43NDYtMjMuMjQ0bDE4LjQ1MS0xNi4zMTgNCgkJYzYuNDgxLDguNjM2LDE3LjUwMiwxNy4wMTcsMzUuMDEzLDE3LjAxN2MxMy42NjksMCwyMS41NzUtNC43NzMsMjEuNTc1LTEyLjkyNGMwLTcuOTM1LTYuNDgtOS4zNy0xNy41MDEtMTEuNzU3bC0xNy4wMzUtMy42MDgNCgkJQzIyLjExNiwxMTcuOTUsNi41NDQsMTA5LjU2OCw2LjU0NCw4OC43MWMwLTI0LjkzMywyNC4xOTUtMzUuNDg3LDQ2LjAxNC0zNS40ODdjMjIuNzg4LDAsNDAuMjcsMTAuMDY5LDQ3LjcyMSwxOC4yMkw4NC40NDYsODguNDU4DQoJCXogTTYyOC4wNywxNDQuMjZWODcuNjYyYzAtMjUuMTY1LTE5LjIwOS0zMy41NDctNDUuMzI0LTMzLjU0N2MtMjcuMzM5LDAtNTAuMzUsMTEuMjUzLTQ5Ljg2NSwzOS4zMTFsMjUuNjUsMS42NjgNCgkJYy0wLjI1My0xMy40MjcsNy42NjUtMTkuODg4LDIzLjAxMi0xOS44ODhjMTQuMTYzLDAsMjAuMzczLDQuNzkyLDIwLjM3MywxNC4zOTZ2MS45MDJjMCw0LjMwNy0xLjE4Myw0Ljc5Mi03LjE5OCw1Ljk5NA0KCQlsLTI1LjE2Niw0LjMyN2MtMTQuODYxLDIuNjQtMjYuODM0LDcuMTgtMzMuOCwxNS41NjJjLTQuMDczLDUuMDQ1LTYuNDc5LDExLjUyNC02LjQ3OSwyMC4xNTkNCgkJYzAsMjQuOTMzLDE4LjIxOSwzNS45NTMsNDAuMjc4LDM1Ljk1M2MxOS42NTUsMCwyNy4zOTYtOS43MDEsMzIuNDAzLTE0LjY0OHYxMS4xNzZoMjYuMDk4di0yNS42MzENCgkJQzYyOC4wNTMsMTQ0LjM1Nyw2MjguMDcsMTQ0LjMsNjI4LjA3LDE0NC4yNiBNNjAxLjkxNiwxMjkuODYzYzAsMTUuNTgtMTAuMjg0LDIzLjAxMi0yNS4xNjYsMjMuMDEyDQoJCWMtMTMuNDI3LDAtMTkuNjUzLTYuMjI5LTE5LjY1My0xNS41ODFjMC05LjExOCw1Ljc2Mi0xMi40NzYsMTguNzA0LTE1LjA5NWwxMC41MzMtMi4xNTRjOC4xNS0xLjY2OCwxMS41MDctMi42NTcsMTUuNTgyLTQuMDczDQoJCVYxMjkuODYzeiIvPg0KCTxwYXRoIGZpbGw9IiMyNDEzNjMiIGQ9Ik0xMDM2LjUyNSwxNDkuMzgxYy0yLjg4NCwwLjQ4LTcuMjA5LDAuOTYxLTEyLjAxNiwwLjk2MWMtNC44MDYsMC05LjM3MS0wLjcyMS0xMS41MzQtMy4xMjQNCgkJYy0yLjE2My0yLjE2Mi0zLjEyNC01LjI4Ni0zLjEyNC0xMS43NzRWNzcuMDUyaDI2LjY3NFY1NS42NjZoLTI2LjY3NFY5Ljc3bC0yNy4zOTMsNi4wMDd2MzkuODloLTI0LjUxdjIxLjM4NmgyNC41MXY2Mi45NTgNCgkJYzAsMTEuMDUzLDAuMjQsMTcuNTQxLDQuODA1LDIzLjA2N2M2LjI0OCw3LjY4OSwxNy43ODIsOS44NTMsMjguODM2LDkuODUzYzguMTcsMCwxNS4xMzktMC43MjEsMjAuNDI2LTEuOTIyVjE0OS4zODF6DQoJCSBNODk5LjMxNiw3NC44OWMxNS4zNzksMCwyNi4xOTEsMTAuMDkyLDI2LjkxMiwyNC45OWgtNTUuNzQ5Qzg3MS4yMDEsODYuMTgzLDg4My42OTcsNzQuODksODk5LjMxNiw3NC44OSBNOTM0Ljg3OSwxMjkuNjc3DQoJCWMtMy42MDQsNS43NjctMTIuNzM1LDE5LjcwNC0zMy40MDEsMTkuNzA0Yy0yMC4xODQsMC0yOS43OTYtMTMuNDU2LTMxLjcxOS0yOS4wNzZoODMuMzgzYzAtMS45MjIsMC4yNC01LjA0NiwwLjI0LTYuOTY4DQoJCWMwLTM1LjA4My0xNy41NDEtNjAuMzE0LTU0LjA2NS02MC4zMTRjLTMxLjk2MSwwLTU2LjIyOSwyMy41NDktNTYuMjI5LDU4Ljg3MmMwLDM3LjAwNSwyMy4wNjgsNjEuMDM1LDU2Ljk1LDYxLjAzNQ0KCQljMzMuNCwwLDQ4Ljc3OS0yMC45MDYsNTMuODI2LTI4LjgzNUw5MzQuODc5LDEyOS42Nzd6IE03NDAuOTYyLDYzLjgzNmMtNi43MjgtNy40NDktMTYuNTgtMTEuMDU1LTI4LjM1NS0xMS4wNTUNCgkJYy0xOC4wMjEsMC0zMC4yNzYsNy45MzEtMzUuODA0LDE3LjU0MlY1NS42NjZINjQ5LjQxdjExNC4zOGgyNy4zOTN2LTYxLjI3NWMwLTE4LjUwMyw3LjkzLTMxLjcxOSwyNC43NTEtMzEuNzE5DQoJCWMyMC42NjUsMCwyMi4xMDcsMTQuNDE4LDIyLjEwNywzNS4wODN2NTcuOTExaDI3LjM5M3YtNjEuMjc1YzAtMTguNTAzLDcuNjktMzEuNzE5LDI0Ljc1MS0zMS43MTkNCgkJYzIwLjY2NSwwLDIyLjEwNywxNC40MTgsMjIuMTA3LDM1LjA4M3Y1Ny45MTFoMjcuMzk0di02NC44NzljMC0yNS4yMzEtMi44ODQtMzMuMTYxLTEwLjA5My00MS4zMzENCgkJYy02LjAwNy02LjcyOS0xNS42MTktMTEuMDU1LTMwLjI3Ny0xMS4wNTVjLTIwLjY2NSwwLTMyLjE5OSw5LjM3Mi0zOS4xNjgsMTcuNzgyQzc0NC4zMjYsNjguMTYsNzQyLjg4NCw2NS45OTksNzQwLjk2Miw2My44MzYiLz4NCjwvZz4NCjwvc3ZnPg0K" alt="NorNet" height="64" /> |
|
</div> |
|
</div> |
|
""") |
|
gradio.Markdown('## Settings') |
|
with gradio.Row(): |
|
sliderNumberOfECGs = gradio.Slider(1, 100, label="Number of ECGs", step = 1, value = 4, interactive = True) |
|
|
|
dropdownType = gradio.Dropdown( [ 'ECG-12', 'ECG-8' ], label = 'ECG Type', interactive = True) |
|
dropdownGeneratorModel = gradio.Dropdown( [ 'Default' ], label = 'Generator Model', interactive = True) |
|
with gradio.Column(): |
|
buttonGenerate = gradio.Button("Generate ECGs!") |
|
buttonAnalyze = gradio.Button("Analyze this ECG!") |
|
with gradio.Row(): |
|
buttonCSV = gradio.DownloadButton("Download CSV") |
|
buttonCSV_hidden = gradio.DownloadButton(visible=False, elem_id="download_csv_hidden") |
|
buttonPDF = gradio.DownloadButton("Download PDF") |
|
buttonPDF_hidden = gradio.DownloadButton(visible=False, elem_id="download_pdf_hidden") |
|
gradio.Markdown('## Output') |
|
with gradio.Row(): |
|
outputGallery = gradio.Gallery(label = 'output', |
|
columns = [ 1 ], |
|
height = 'auto', |
|
show_label = True, |
|
preview = True) |
|
outputGallery.select(select) |
|
gradio.Markdown('## Analysis') |
|
|
|
|
|
buttonGenerate.click(predict, |
|
inputs = [ sliderNumberOfECGs, |
|
|
|
dropdownType, |
|
dropdownGeneratorModel ], |
|
outputs = [ outputGallery ] |
|
) |
|
|
|
|
|
buttonAnalyze.click(analyze) |
|
|
|
|
|
|
|
|
|
buttonCSV.click(downloadCSV) |
|
buttonCSV.click(fn = downloadCSV, inputs = None, outputs = [ buttonCSV_hidden ]).then( |
|
fn = None, inputs = None, outputs = None, |
|
js = "() => document.querySelector('#download_csv_hidden').click()") |
|
buttonPDF.click(downloadPDF) |
|
buttonPDF.click(fn = downloadPDF, inputs = None, outputs = [ buttonPDF_hidden ]).then( |
|
fn = None, inputs = None, outputs = None, |
|
js = "() => document.querySelector('#download_pdf_hidden').click()") |
|
|
|
|
|
gui.load(predict, |
|
inputs = [ sliderNumberOfECGs, |
|
|
|
dropdownType, |
|
dropdownGeneratorModel ], |
|
outputs = [ outputGallery ] |
|
) |
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
|
TempDirectory = tempfile.TemporaryDirectory(prefix = 'DeepFakeECGPlus-') |
|
log(f'Prepared temporary directory {TempDirectory.name}') |
|
|
|
|
|
gui.launch(allowed_paths = [ TempDirectory.name ]) |
|
|
|
|
|
log(f'Cleaning up temporary directory {TempDirectory.name}') |
|
TempDirectory.cleanup() |
|
log('Done!') |
|
|