Franny Dean commited on
Commit
97f0f9a
1 Parent(s): 5fa2298

attempt animation

Browse files
.DS_Store CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
 
.ipynb_checkpoints/app-checkpoint.py ADDED
@@ -0,0 +1,570 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import matplotlib.pyplot as plt
4
+ from scipy.integrate import odeint
5
+ import torch
6
+ from torch.utils import data
7
+ from torch.utils.data import DataLoader, Dataset
8
+ from torch import nn, optim
9
+ from skimage.transform import rescale, resize
10
+ from torch import nn, optim
11
+ import torch.nn.functional as F
12
+ from torch.utils.data import Subset
13
+ from scipy.interpolate import interp1d
14
+ import collections
15
+ import numpy as np
16
+ import random
17
+
18
+ #for pvloop simulator:
19
+ import pandas as pd
20
+ from scipy.integrate import odeint
21
+ import torchvision
22
+ import echonet
23
+ import matplotlib.animation as animation
24
+
25
+ device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
26
+
27
+ sequences_all = []
28
+ info_data_all = []
29
+ path = 'EchoNet-Dynamic'
30
+ output_path = ''
31
+
32
+ class Echo(torchvision.datasets.VisionDataset):
33
+ """EchoNet-Dynamic Dataset.
34
+ Args:
35
+ root (string): Root directory of dataset (defaults to `echonet.config.DATA_DIR`)
36
+ split (string): One of {``train'', ``val'', ``test'', ``all'', or ``external_test''}
37
+ target_type (string or list, optional): Type of target to use,
38
+ ``Filename'', ``EF'', ``EDV'', ``ESV'', ``LargeIndex'',
39
+ ``SmallIndex'', ``LargeFrame'', ``SmallFrame'', ``LargeTrace'',
40
+ or ``SmallTrace''
41
+ Can also be a list to output a tuple with all specified target types.
42
+ The targets represent:
43
+ ``Filename'' (string): filename of video
44
+ ``EF'' (float): ejection fraction
45
+ ``EDV'' (float): end-diastolic volume
46
+ ``ESV'' (float): end-systolic volume
47
+ ``LargeIndex'' (int): index of large (diastolic) frame in video
48
+ ``SmallIndex'' (int): index of small (systolic) frame in video
49
+ ``LargeFrame'' (np.array shape=(3, height, width)): normalized large (diastolic) frame
50
+ ``SmallFrame'' (np.array shape=(3, height, width)): normalized small (systolic) frame
51
+ ``LargeTrace'' (np.array shape=(height, width)): left ventricle large (diastolic) segmentation
52
+ value of 0 indicates pixel is outside left ventricle
53
+ 1 indicates pixel is inside left ventricle
54
+ ``SmallTrace'' (np.array shape=(height, width)): left ventricle small (systolic) segmentation
55
+ value of 0 indicates pixel is outside left ventricle
56
+ 1 indicates pixel is inside left ventricle
57
+ Defaults to ``EF''.
58
+ mean (int, float, or np.array shape=(3,), optional): means for all (if scalar) or each (if np.array) channel.
59
+ Used for normalizing the video. Defaults to 0 (video is not shifted).
60
+ std (int, float, or np.array shape=(3,), optional): standard deviation for all (if scalar) or each (if np.array) channel.
61
+ Used for normalizing the video. Defaults to 0 (video is not scaled).
62
+ length (int or None, optional): Number of frames to clip from video. If ``None'', longest possible clip is returned.
63
+ Defaults to 16.
64
+ period (int, optional): Sampling period for taking a clip from the video (i.e. every ``period''-th frame is taken)
65
+ Defaults to 2.
66
+ max_length (int or None, optional): Maximum number of frames to clip from video (main use is for shortening excessively
67
+ long videos when ``length'' is set to None). If ``None'', shortening is not applied to any video.
68
+ Defaults to 250.
69
+ clips (int, optional): Number of clips to sample. Main use is for test-time augmentation with random clips.
70
+ Defaults to 1.
71
+ pad (int or None, optional): Number of pixels to pad all frames on each side (used as augmentation).
72
+ and a window of the original size is taken. If ``None'', no padding occurs.
73
+ Defaults to ``None''.
74
+ noise (float or None, optional): Fraction of pixels to black out as simulated noise. If ``None'', no simulated noise is added.
75
+ Defaults to ``None''.
76
+ target_transform (callable, optional): A function/transform that takes in the target and transforms it.
77
+ external_test_location (string): Path to videos to use for external testing.
78
+ """
79
+
80
+ def __init__(self, root=None,
81
+ split="train", target_type="EF",
82
+ mean=0., std=1.,
83
+ length=16, period=2,
84
+ max_length=250,
85
+ clips=1,
86
+ pad=None,
87
+ noise=None,
88
+ target_transform=None,
89
+ external_test_location=None):
90
+ if root is None:
91
+ root = path
92
+
93
+ super().__init__(root, target_transform=target_transform)
94
+
95
+ self.split = split.upper()
96
+ if not isinstance(target_type, list):
97
+ target_type = [target_type]
98
+ self.target_type = target_type
99
+ self.mean = mean
100
+ self.std = std
101
+ self.length = length
102
+ self.max_length = max_length
103
+ self.period = period
104
+ self.clips = clips
105
+ self.pad = pad
106
+ self.noise = noise
107
+ self.target_transform = target_transform
108
+ self.external_test_location = external_test_location
109
+
110
+ self.fnames, self.outcome = [], []
111
+
112
+ if self.split == "EXTERNAL_TEST":
113
+ self.fnames = sorted(os.listdir(self.external_test_location))
114
+ else:
115
+ # Load video-level labels
116
+ with open(f"{self.root}/FileList.csv") as f:
117
+ data = pd.read_csv(f)
118
+ data["Split"].map(lambda x: x.upper())
119
+
120
+ if self.split != "ALL":
121
+ data = data[data["Split"] == self.split]
122
+
123
+ self.header = data.columns.tolist()
124
+ self.fnames = data["FileName"].tolist()
125
+ self.fnames = [fn + ".avi" for fn in self.fnames if os.path.splitext(fn)[1] == ""] # Assume avi if no suffix
126
+ self.outcome = data.values.tolist()
127
+
128
+ # Check that files are present
129
+ """
130
+ missing = set(self.fnames) - set(os.listdir(os.path.join(self.root, "Videos")))
131
+ if len(missing) != 0:
132
+ print("{} videos could not be found in {}:".format(len(missing), os.path.join(self.root, "Videos")))
133
+ for f in sorted(missing):
134
+ print("\t", f)
135
+ raise FileNotFoundError(os.path.join(self.root, "Videos", sorted(missing)[0]))
136
+ """
137
+
138
+ # Load traces
139
+ self.frames = collections.defaultdict(list)
140
+ self.trace = collections.defaultdict(_defaultdict_of_lists)
141
+
142
+ with open(f"{self.root}/VolumeTracings.csv") as f:
143
+ header = f.readline().strip().split(",")
144
+ assert header == ["FileName", "X1", "Y1", "X2", "Y2", "Frame"]
145
+
146
+ for line in f:
147
+ filename, x1, y1, x2, y2, frame = line.strip().split(',')
148
+ x1 = float(x1)
149
+ y1 = float(y1)
150
+ x2 = float(x2)
151
+ y2 = float(y2)
152
+ frame = int(frame)
153
+ if frame not in self.trace[filename]:
154
+ self.frames[filename].append(frame)
155
+ self.trace[filename][frame].append((x1, y1, x2, y2))
156
+ for filename in self.frames:
157
+ for frame in self.frames[filename]:
158
+ self.trace[filename][frame] = np.array(self.trace[filename][frame])
159
+
160
+ # A small number of videos are missing traces; remove these videos
161
+ keep = [len(self.frames[f]) >= 2 for f in self.fnames]
162
+ self.fnames = [f for (f, k) in zip(self.fnames, keep) if k]
163
+ self.outcome = [f for (f, k) in zip(self.outcome, keep) if k]
164
+
165
+ def __getitem__(self, index):
166
+ # Find filename of video
167
+ if self.split == "EXTERNAL_TEST":
168
+ video = os.path.join(self.external_test_location, self.fnames[index])
169
+ elif self.split == "CLINICAL_TEST":
170
+ video = os.path.join(self.root, "ProcessedStrainStudyA4c", self.fnames[index])
171
+ else:
172
+ video = os.path.join(self.root, "Videos", self.fnames[index])
173
+
174
+ # Load video into np.array
175
+ video = echonet.utils.loadvideo(video).astype(np.float32)
176
+
177
+ # Add simulated noise (black out random pixels)
178
+ # 0 represents black at this point (video has not been normalized yet)
179
+ if self.noise is not None:
180
+ n = video.shape[1] * video.shape[2] * video.shape[3]
181
+ ind = np.random.choice(n, round(self.noise * n), replace=False)
182
+ f = ind % video.shape[1]
183
+ ind //= video.shape[1]
184
+ i = ind % video.shape[2]
185
+ ind //= video.shape[2]
186
+ j = ind
187
+ video[:, f, i, j] = 0
188
+
189
+ # Apply normalization
190
+ if isinstance(self.mean, (float, int)):
191
+ video -= self.mean
192
+ else:
193
+ video -= self.mean.reshape(3, 1, 1, 1)
194
+
195
+ if isinstance(self.std, (float, int)):
196
+ video /= self.std
197
+ else:
198
+ video /= self.std.reshape(3, 1, 1, 1)
199
+
200
+ # Set number of frames
201
+ c, f, h, w = video.shape
202
+ if self.length is None:
203
+ # Take as many frames as possible
204
+ length = f // self.period
205
+ else:
206
+ # Take specified number of frames
207
+ length = self.length
208
+
209
+ if self.max_length is not None:
210
+ # Shorten videos to max_length
211
+ length = min(length, self.max_length)
212
+
213
+ if f < length * self.period:
214
+ # Pad video with frames filled with zeros if too short
215
+ # 0 represents the mean color (dark grey), since this is after normalization
216
+ video = np.concatenate((video, np.zeros((c, length * self.period - f, h, w), video.dtype)), axis=1)
217
+ c, f, h, w = video.shape # pylint: disable=E0633
218
+
219
+ if self.clips == "all":
220
+ # Take all possible clips of desired length
221
+ start = np.arange(f - (length - 1) * self.period)
222
+ else:
223
+ # Take random clips from video
224
+ start = np.random.choice(f - (length - 1) * self.period, self.clips)
225
+
226
+ # Gather targets
227
+ target = []
228
+ for t in self.target_type:
229
+ key = self.fnames[index]
230
+ if t == "Filename":
231
+ target.append(self.fnames[index])
232
+ elif t == "LargeIndex":
233
+ # Traces are sorted by cross-sectional area
234
+ # Largest (diastolic) frame is last
235
+ target.append(int(self.frames[key][-1]))
236
+ elif t == "SmallIndex":
237
+ # Largest (diastolic) frame is first
238
+ target.append(int(self.frames[key][0]))
239
+ elif t == "LargeFrame":
240
+ target.append(video[:, self.frames[key][-1], :, :])
241
+ elif t == "SmallFrame":
242
+ target.append(video[:, self.frames[key][0], :, :])
243
+ elif t in ["LargeTrace", "SmallTrace"]:
244
+ if t == "LargeTrace":
245
+ t = self.trace[key][self.frames[key][-1]]
246
+ else:
247
+ t = self.trace[key][self.frames[key][0]]
248
+ x1, y1, x2, y2 = t[:, 0], t[:, 1], t[:, 2], t[:, 3]
249
+ x = np.concatenate((x1[1:], np.flip(x2[1:])))
250
+ y = np.concatenate((y1[1:], np.flip(y2[1:])))
251
+
252
+ r, c = skimage.draw.polygon(np.rint(y).astype(np.int), np.rint(x).astype(np.int), (video.shape[2], video.shape[3]))
253
+ mask = np.zeros((video.shape[2], video.shape[3]), np.float32)
254
+ mask[r, c] = 1
255
+ target.append(mask)
256
+ else:
257
+ if self.split == "CLINICAL_TEST" or self.split == "EXTERNAL_TEST":
258
+ target.append(np.float32(0))
259
+ else:
260
+ target.append(np.float32(self.outcome[index][self.header.index(t)]))
261
+
262
+ if target != []:
263
+ target = tuple(target) if len(target) > 1 else target[0]
264
+ if self.target_transform is not None:
265
+ target = self.target_transform(target)
266
+
267
+ # Select clips from video
268
+ video = tuple(video[:, s + self.period * np.arange(length), :, :] for s in start)
269
+ if self.clips == 1:
270
+ video = video[0]
271
+ else:
272
+ video = np.stack(video)
273
+
274
+ if self.pad is not None:
275
+ # Add padding of zeros (mean color of videos)
276
+ # Crop of original size is taken out
277
+ # (Used as augmentation)
278
+ c, l, h, w = video.shape
279
+ temp = np.zeros((c, l, h + 2 * self.pad, w + 2 * self.pad), dtype=video.dtype)
280
+ temp[:, :, self.pad:-self.pad, self.pad:-self.pad] = video # pylint: disable=E1130
281
+ i, j = np.random.randint(0, 2 * self.pad, 2)
282
+ video = temp[:, :, i:(i + h), j:(j + w)]
283
+
284
+ return video, target
285
+
286
+ def __len__(self):
287
+ return len(self.fnames)
288
+
289
+ def extra_repr(self) -> str:
290
+ """Additional information to add at end of __repr__."""
291
+ lines = ["Target type: {target_type}", "Split: {split}"]
292
+ return '\n'.join(lines).format(**self.__dict__)
293
+
294
+
295
+ def _defaultdict_of_lists():
296
+ """Returns a defaultdict of lists.
297
+ This is used to avoid issues with Windows (if this function is anonymous,
298
+ the Echo dataset cannot be used in a dataloader).
299
+ """
300
+
301
+ return collections.defaultdict(list)
302
+ ##
303
+ print("Done loading training data!")
304
+ # define normalization layer to make sure output xi in an interval [ai, bi]:
305
+ # define normalization layer to make sure output xi in an interval [ai, bi]:
306
+
307
+
308
+ class IntervalNormalizationLayer(torch.nn.Module):
309
+ def __init__(self):
310
+ super().__init__()
311
+ # new_output = [Tc, start_p, Emax, Emin, Rm, Ra, Vd]
312
+ self.a = torch.tensor([0.4, 0., 0.5, 0.02, 0.005, 0.0001, 4.], dtype=torch.float32) #HR in 20-200->Tc in [0.3, 4]
313
+ self.b = torch.tensor([1.7, 280., 3.5, 0.1, 0.1, 0.25, 16.], dtype=torch.float32)
314
+ #taken out (initial conditions): a: 20, 5, 50; b: 400, 20, 100
315
+ def forward(self, inputs):
316
+ sigmoid_output = torch.sigmoid(inputs)
317
+ scaled_output = sigmoid_output * (self.b - self.a) + self.a
318
+ return scaled_output
319
+
320
+ class NEW3DCNN(nn.Module):
321
+ def __init__(self, num_parameters):
322
+ super(NEW3DCNN, self).__init__()
323
+
324
+ self.conv1 = nn.Conv3d(3, 8, kernel_size=3, padding=1)
325
+ self.batchnorm1 = nn.BatchNorm3d(8)
326
+ self.conv2 = nn.Conv3d(8, 16, kernel_size=3, padding=1)
327
+ self.batchnorm2 = nn.BatchNorm3d(16)
328
+ self.conv3 = nn.Conv3d(16, 32, kernel_size=3, padding=1)
329
+ self.batchnorm3 = nn.BatchNorm3d(32)
330
+ self.conv4 = nn.Conv3d(32, 64, kernel_size=3, padding=1)
331
+ self.batchnorm4 = nn.BatchNorm3d(64)
332
+ self.conv5 = nn.Conv3d(64, 128, kernel_size=3, padding=1)
333
+ self.batchnorm5 = nn.BatchNorm3d(128)
334
+ self.pool = nn.AdaptiveAvgPool3d(1)
335
+ self.fc1 = nn.Linear(128, 512)
336
+ self.fc2 = nn.Linear(512, num_parameters)
337
+ self.norm1 = IntervalNormalizationLayer()
338
+
339
+ def forward(self, x):
340
+ x = F.relu(self.batchnorm1(self.conv1(x)))
341
+ x = F.max_pool3d(x, kernel_size=2, stride=2)
342
+ x = F.relu(self.batchnorm2(self.conv2(x)))
343
+ x = F.max_pool3d(x, kernel_size=2, stride=2)
344
+ x = F.relu(self.batchnorm3(self.conv3(x)))
345
+ x = F.max_pool3d(x, kernel_size=2, stride=2)
346
+ x = F.relu(self.batchnorm4(self.conv4(x)))
347
+ x = F.max_pool3d(x, kernel_size=2, stride=2)
348
+ x = F.relu(self.batchnorm5(self.conv5(x)))
349
+ x = self.pool(x)
350
+ x = x.view(x.size(0), -1)
351
+ x = F.relu(self.fc1(x))
352
+ x = self.fc2(x)
353
+ x = self.norm1(x)
354
+
355
+ return x
356
+
357
+
358
+ # Define a neural network with one hidden layer
359
+ class Interpolator(nn.Module):
360
+ def __init__(self):
361
+ super().__init__()
362
+ self.fc1 = nn.Linear(6, 250).double()
363
+ self.fc2 = nn.Linear(250, 2).double()
364
+
365
+ def forward(self, x):
366
+ x = torch.relu(self.fc1(x))
367
+ x = self.fc2(x)
368
+ return x
369
+
370
+ # Initialize the neural network
371
+ net = Interpolator()
372
+ net.load_state_dict(torch.load('final_model_weights/interp6_7param_weight.pt'))
373
+ print("Done loading interpolator!")
374
+
375
+ weights_path = 'final_model_weights/202_full_echonet_7param_Vloss_epoch_200_lr_0.001_weight_best_model.pt'
376
+ model = NEW3DCNN(num_parameters = 7)
377
+ model.load_state_dict(torch.load(weights_path))
378
+ model.to(device)
379
+
380
+ ## PV loops
381
+
382
+ #returns Plv at time t using Elastance(t) and Vlv(t)-Vd=x1
383
+ def Plv(volume, Emax, Emin, t, Tc, Vd):
384
+ return Elastance(Emax,Emin,t, Tc)*(volume - Vd)
385
+
386
+ #returns Elastance(t)
387
+ def Elastance(Emax,Emin, t, Tc):
388
+ t = t-int(t/Tc)*Tc #can remove this if only want 1st ED (and the 1st ES before)
389
+ tn = t/(0.2+0.15*Tc)
390
+ return (Emax-Emin)*1.55*(tn/0.7)**1.9/((tn/0.7)**1.9+1)*1/((tn/1.17)**21.9+1) + Emin
391
+
392
+ def solve_ODE_for_volume(Rm, Ra, Emax, Emin, Vd, Tc, start_v, t):
393
+
394
+ # the ODE from Simaan et al 2008
395
+ def heart_ode(y, t, Rs, Rm, Ra, Rc, Ca, Cs, Cr, Ls, Emax, Emin, Tc):
396
+ x1, x2, x3, x4, x5 = y #here y is a vector of 5 values (not functions), at time t, used for getting (dy/dt)(t)
397
+ P_lv = Plv(x1+Vd,Emax,Emin,t,Tc,Vd)
398
+ dydt = [r(x2-P_lv)/Rm-r(P_lv-x4)/Ra, (x3-x2)/(Rs*Cr)-r(x2-P_lv)/(Cr*Rm), (x2-x3)/(Rs*Cs)+x5/Cs, -x5/Ca+r(P_lv-x4)/(Ca*Ra), (x4-x3-Rc*x5)/Ls]
399
+ return dydt
400
+
401
+ # RELU for diodes
402
+ def r(u):
403
+ return max(u, 0.)
404
+
405
+ # Define fixed parameters
406
+ Rs = 1.0
407
+ Rc = 0.0398
408
+ Ca = 0.08
409
+ Cs = 1.33
410
+ Cr = 4.400
411
+ Ls = 0.0005
412
+ startp = 75.
413
+
414
+ # Initial conditions
415
+ start_pla = float(start_v*Elastance(Emax, Emin, 0, Tc))
416
+ start_pao = startp
417
+ start_pa = start_pao
418
+ start_qt = 0 #aortic flow is Q_T and is 0 at ED, also see Fig5 in simaan2008dynamical
419
+ y0 = [start_v, start_pla, start_pa, start_pao, start_qt]
420
+
421
+ # Solve
422
+ sol = odeint(heart_ode, y0, t, args = (Rs, Rm, Ra, Rc, Ca, Cs, Cr, Ls, Emax, Emin, Tc)) #t: list of values
423
+
424
+ # volume is the first state variable plus theoretical zero pressure volume
425
+ volumes = np.array(sol[:, 0]) + Vd
426
+
427
+ return volumes
428
+
429
+ def pvloop_simulator(Rm, Ra, Emax, Emin, Vd, Tc, start_v):
430
+
431
+
432
+ # Define initial parameters
433
+ init_Emax = Emax # 3.0 # .5 to 3.5
434
+ init_Emin = Emin # 0.04 # .02 to .1
435
+ # init_Tc = Tc # .4 # .4 to 1.7
436
+ init_Vd = Vd # 10.0 # 0 to 25
437
+
438
+ # DUMMY VOLUME
439
+ # def volume(t, Tc):
440
+ # return 50*np.sin(2 * np.pi * t*(1/Tc))+100
441
+
442
+ # SOLVE the ODE model for the VOLUME CURVE
443
+ N = 100
444
+ t = np.linspace(0, Tc*N, int(60000*N)) #np.linspace(1, 100, 1000000)
445
+ volumes = solve_ODE_for_volume(Rm, Ra, Emax, Emin, Vd, Tc, start_v, t)
446
+
447
+ # FUNCTIONS for PRESSURE CURVE
448
+ vectorized_Elastance = np.vectorize(Elastance)
449
+ vectorized_Plv = np.vectorize(Plv)
450
+
451
+ def pressure(t, volume, Emax, Emin, Tc, Vd):
452
+ return vectorized_Plv(volume, Emax, Emin, t, Tc, Vd)
453
+
454
+ # calculate PRESSURE
455
+ pressures = pressure(t, volumes, init_Emax, init_Emin, Tc, init_Vd)
456
+
457
+ # Create the figure and the loop that we will manipulate
458
+ fig, ax = plt.subplots()
459
+ plt.ylim((0,220))
460
+ plt.xlim((0,250))
461
+ line = ax.plot(volumes[(N-2)*60000], pressures[(N-2)*60000], lw=1)
462
+ #line = ax.plot(volumes[(N-2)*60000:(N)*60000], pressures[(N-2)*60000:(N)*60000], lw=1)
463
+ #print(line)
464
+ line = line[0]
465
+ #print(line)
466
+
467
+ fig.suptitle('Predicted PI-SSL LV Pressure Volume Loop', fontsize=16)
468
+ #plt.rcParams['fig.suptitle'] = -2.0
469
+ #ax.set_title(f'Mitral valve circuit resistance (Rm): {Rm} mmHg*s/ml \n Aortic valve circuit resistance (Ra): {Ra} mmHg*s/ml', fontsize=6)
470
+ ax.set_xlabel('LV Volume (ml)')
471
+ ax.set_ylabel('LV Pressure (mmHg)')
472
+
473
+ # adjust the main plot to make room for the sliders
474
+ # fig.subplots_adjust(left=0.25, bottom=0.25)
475
+
476
+ def update(frame):
477
+ # for each frame, update the data stored on each artist.
478
+ x = volumes[:(N-2)*60000+frame]
479
+ y = pressures[:(N-2)*60000:(N)+frame]
480
+ line.set_xdata(x)
481
+ line.set_ydata(y)
482
+ return line
483
+
484
+ anim = animation.FuncAnimation(fig=fig, func=update, frames=40, interval=30)
485
+
486
+ return plt, Rm, Ra, Emax, Emin, Vd, Tc, start_v
487
+
488
+ def pvloop_simulator_plot_only(Rm, Ra, Emax, Emin, Vd, Tc, start_v):
489
+ plot,_,_,_,_,_,_,_ =pvloop_simulator(Rm, Ra, Emax, Emin, Vd, Tc, start_v)
490
+ return plot
491
+
492
+ ## Demo
493
+
494
+ def generate_example():
495
+ # get random input
496
+ data_path = 'EchoNet-Dynamic'
497
+ image_data = Echo(root = data_path, split = 'all', target_type=['Filename','LargeIndex','SmallIndex'])
498
+ image_loaded_data = DataLoader(image_data, batch_size=30, shuffle=True)
499
+ val_data = next(iter(image_loaded_data))
500
+ #create_echo_clip(val_data,'test')
501
+ val_seq = val_data[0]
502
+
503
+ val_tensor = torch.tensor(val_seq, dtype=torch.float32)
504
+ n=random.randint(0, 29)
505
+ results = model(val_tensor)[n]
506
+
507
+ filename = val_data[1][0][n]
508
+ video = f"EchoNet-Dynamic/Videos/{filename}"
509
+
510
+ plot, Rm, Ra, Emax, Emin, Vd,Tc, start_v = pvloop_simulator(Rm=round(results[4].item(),2), Ra=round(results[5].item(),2), Emax=results[2].item(), Emin=round(results[3].item(),2), Vd=round(results[6].item(),2), Tc=round(results[0].item(),2), start_v=round(results[1].item(),2))
511
+ video = video.replace("avi", "mp4")
512
+ return video, plot, Rm, Ra, Emax, Emin, Vd, Tc, start_v
513
+
514
+ title = "Physics-informed self-supervised learning for predicting cardiac digital twins with echocardiography"
515
+
516
+ description = """
517
+ <p style='text-align: center'> Keying Kuang, Frances Dean, Jack B. Jedlicki, David Ouyang, Anthony Philippakis, David Sontag, Ahmed Alaa <br></p>
518
+ <p> We develop methodology for predicting digital twins from non-invasive cardiac ultrasound images in <a href='https://arxiv.org/abs/2403.00177'>Non-Invasive Medical Digital Twins using Physics-Informed Self-Supervised Learning</a>. Check out our <a href='https://github.com/AlaaLab/CardioPINN' target='_blank'>code.</a> \n \n
519
+ We demonstrate the ability of our model to predict left ventricular pressure-volume loops using image data here. To run example predictions on samples from the <a href='https://echonet.github.io/dynamic/'>EchoNet</a> dataset, click the first button. \n \n
520
+ Below you can input values of predicted parameters and output a simulated pressure-volume loop predicted by the <a href='https://ieeexplore.ieee.org/document/4729737/keywords#keywords'>Simaan et al 2008</a> hydraulic analogy model by pressing 'Run simulation.'</p>
521
+ """
522
+
523
+ gr.Markdown("<h1 style='text-align: center; margin-bottom: 1rem'>" + title + "</h1>")
524
+ gr.Markdown(description)
525
+
526
+ with gr.Blocks() as demo:
527
+
528
+ # text
529
+ gr.Markdown("<h1 style='text-align: center; margin-bottom: 1rem'>" + title + "</h1>")
530
+ gr.Markdown(description)
531
+
532
+ with gr.Row():
533
+ with gr.Column(scale=1.5, min_width=100):
534
+
535
+ generate_button = gr.Button("Load sample echocardiogram and generate result")
536
+ with gr.Row():
537
+ video = gr.PlayableVideo() #format="avi"
538
+ plot = gr.Plot()
539
+
540
+ with gr.Row():
541
+ Rm = gr.Number(label="Mitral valve circuit resistance (Rm) mmHg*s/ml:")
542
+ Ra = gr.Number(label="Aortic valve circuit resistance (Ra) mmHg*s/ml:")
543
+ Emax = gr.Number(label="Maximum elastance (Emax) mmHg/ml:")
544
+ Emin = gr.Number(label="Minimum elastance (Emin) mmHg/ml:")
545
+ Vd = gr.Number(label="Theoretical zero pressure volume (Vd) ml:")
546
+ Tc = gr.Number(label="Cycle duration (Tc) s:")
547
+ start_v = gr.Number(label="Initial volume (start_v) ml:")
548
+
549
+ simulation_button = gr.Button("Run simulation")
550
+
551
+
552
+
553
+ with gr.Row():
554
+ sl1 = gr.Slider(0.005, 0.1, value=Rm, label="Rm")
555
+ sl2 = gr.Slider(0.0001, 0.25, value=Ra, label="Ra")
556
+ sl3 = gr.Slider(0.5, 3.5, value=Emax, label="Emax")
557
+ sl4 = gr.Slider(0.02, 0.1, value= Emin, label="Emin")
558
+ sl5 = gr.Slider(4.0, 25.0, value=Vd, label="Vd")
559
+ sl6 = gr.Slider(0.4, 1.7, value=Tc, label="Tc")
560
+ sl7 = gr.Slider(0.0, 280.0, value=start_v, label="start_v")
561
+
562
+
563
+ generate_button.click(fn=generate_example, outputs = [video,plot,Rm,Ra,Emax,Emin,Vd,Tc,start_v])
564
+
565
+
566
+ simulation_button.click(fn=pvloop_simulator_plot_only, inputs = [sl1,sl2,sl3,sl4,sl5,sl6,sl7], outputs = [gr.Plot()])
567
+
568
+
569
+
570
+ demo.launch()
EchoNet-Dynamic/.DS_Store CHANGED
Binary files a/EchoNet-Dynamic/.DS_Store and b/EchoNet-Dynamic/.DS_Store differ
 
EchoNet-Dynamic/FileList.csv CHANGED
@@ -2,7 +2,6 @@ FileName,EF,ESV,EDV,FrameHeight,FrameWidth,FPS,NumberOfFrames,Split
2
  0X2CE2BBE899FB30A,69.3733509,15.07375645,49.21777894,112,112,50,208,TRAIN
3
  0X310589D4CDC13146,45.54886748,49.07256162,90.12220564,112,112,53,151,TRAIN
4
  0X34D6A77674B8E6F2,55.01383234,30.59875717,68.01814594,112,112,50,166,TRAIN
5
- 0X352D7150FCBFA7C1,28.89583936,58.48777087,82.25646762,112,112,50,61,TRAIN
6
  0X36B33D2B6530F83E,57.65061751,33.64751654,79.45220108,112,112,50,161,VAL
7
  0X3B6528FD917A0E82,58.58439974,38.07170832,91.92600876,112,112,50,201,TRAIN
8
  0X49B360CA6BB04B97,57.87742067,25.5427545,60.63910355,112,112,50,155,TRAIN
@@ -11,7 +10,6 @@ FileName,EF,ESV,EDV,FrameHeight,FrameWidth,FPS,NumberOfFrames,Split
11
  0X55AEA1223ADFB9E8,58.42788325,51.81422337,124.636962,112,112,47,126,VAL
12
  0X5D98E2C64E1625FB,27.7459176,118.5770238,164.1111753,112,112,43,136,TRAIN
13
  0X623F4667A42B8338,60.35354935,30.37659467,76.61869895,112,112,50,127,VAL
14
- 0X6382AE8EB4B73A3D,49.96284792,40.07870013,80.09788419,112,112,73,138,TRAIN
15
  0X63FECBA90BA79F81,64.86754791,21.00386139,59.78478627,112,112,50,198,VAL
16
  0X6532C54260D0231,55.73785825,29.72626047,67.15956186,112,112,50,140,TRAIN
17
  0X665E43AF6FE1563D,65.64105652,21.3883945,62.24986083,112,112,50,148,TRAIN
 
2
  0X2CE2BBE899FB30A,69.3733509,15.07375645,49.21777894,112,112,50,208,TRAIN
3
  0X310589D4CDC13146,45.54886748,49.07256162,90.12220564,112,112,53,151,TRAIN
4
  0X34D6A77674B8E6F2,55.01383234,30.59875717,68.01814594,112,112,50,166,TRAIN
 
5
  0X36B33D2B6530F83E,57.65061751,33.64751654,79.45220108,112,112,50,161,VAL
6
  0X3B6528FD917A0E82,58.58439974,38.07170832,91.92600876,112,112,50,201,TRAIN
7
  0X49B360CA6BB04B97,57.87742067,25.5427545,60.63910355,112,112,50,155,TRAIN
 
10
  0X55AEA1223ADFB9E8,58.42788325,51.81422337,124.636962,112,112,47,126,VAL
11
  0X5D98E2C64E1625FB,27.7459176,118.5770238,164.1111753,112,112,43,136,TRAIN
12
  0X623F4667A42B8338,60.35354935,30.37659467,76.61869895,112,112,50,127,VAL
 
13
  0X63FECBA90BA79F81,64.86754791,21.00386139,59.78478627,112,112,50,198,VAL
14
  0X6532C54260D0231,55.73785825,29.72626047,67.15956186,112,112,50,140,TRAIN
15
  0X665E43AF6FE1563D,65.64105652,21.3883945,62.24986083,112,112,50,148,TRAIN
EchoNet-Dynamic/Videos/.DS_Store CHANGED
Binary files a/EchoNet-Dynamic/Videos/.DS_Store and b/EchoNet-Dynamic/Videos/.DS_Store differ
 
EchoNet-Dynamic/Videos/0X352D7150FCBFA7C1.avi DELETED
Binary file (228 kB)
 
EchoNet-Dynamic/Videos/0X352D7150FCBFA7C1.mp4 DELETED
Binary file (46.7 kB)
 
EchoNet-Dynamic/Videos/0X6382AE8EB4B73A3D.avi DELETED
Binary file (454 kB)
 
EchoNet-Dynamic/Videos/0X6382AE8EB4B73A3D.mp4 DELETED
Binary file (32.9 kB)
 
EchoNet-Dynamic/VolumeTracings.csv CHANGED
@@ -125,48 +125,6 @@ FileName,X1,Y1,X2,Y2,Frame
125
  0X34D6A77674B8E6F2.avi,49.34517087,68.20596855,71.0245205,62.02791555,97
126
  0X34D6A77674B8E6F2.avi,49.63605055,70.06759853,71.56193286,63.8192901,97
127
  0X34D6A77674B8E6F2.avi,59.82206211,69.10937228,71.56568191,65.76274487,97
128
- 0X352D7150FCBFA7C1.avi,49.80208333,13.70833333,66.75520833,54.90625,58
129
- 0X352D7150FCBFA7C1.avi,49.9160378,14.90485525,51.40619797,14.29164775,58
130
- 0X352D7150FCBFA7C1.avi,50.14394674,17.29789908,54.61442725,15.45827657,58
131
- 0X352D7150FCBFA7C1.avi,50.37185567,19.69094292,57.82265652,16.6249054,58
132
- 0X352D7150FCBFA7C1.avi,50.37217339,22.17764154,61.0308858,17.79153423,58
133
- 0X352D7150FCBFA7C1.avi,49.84301648,24.88222133,64.23911507,18.95816306,58
134
- 0X352D7150FCBFA7C1.avi,49.31385957,27.58680111,66.76347424,20.40620746,58
135
- 0X352D7150FCBFA7C1.avi,48.92888294,30.23205006,68.60351955,22.13585004,58
136
- 0X352D7150FCBFA7C1.avi,49.12000837,32.64023047,70.44356485,23.86549263,58
137
- 0X352D7150FCBFA7C1.avi,49.3111338,35.04841088,72.28361015,25.59513521,58
138
- 0X352D7150FCBFA7C1.avi,49.50225923,37.45659129,74.12365546,27.3247778,58
139
- 0X352D7150FCBFA7C1.avi,49.69338466,39.8647717,75.41063515,29.28200933,58
140
- 0X352D7150FCBFA7C1.avi,49.78129389,42.31542603,76.23844775,31.42819015,58
141
- 0X352D7150FCBFA7C1.avi,49.73308878,44.82209202,77.06626035,33.57437097,58
142
- 0X352D7150FCBFA7C1.avi,49.68488366,47.32875801,77.89407295,35.72055179,58
143
- 0X352D7150FCBFA7C1.avi,49.63667855,49.83542399,78.72188556,37.86673261,58
144
- 0X352D7150FCBFA7C1.avi,50.4411498,51.99120988,79.54969816,40.01291343,58
145
- 0X352D7150FCBFA7C1.avi,51.70538648,53.95780026,79.65767974,42.4553079,58
146
- 0X352D7150FCBFA7C1.avi,52.96962315,55.92439065,79.2872573,45.09456774,58
147
- 0X352D7150FCBFA7C1.avi,54.23385983,57.89098103,78.91683487,47.73382758,58
148
- 0X352D7150FCBFA7C1.avi,69.24867506,54.1991475,78.54641244,50.37308742,58
149
- 0X352D7150FCBFA7C1.avi,52.171875,16.98958333,64.56770833,53.99479167,46
150
- 0X352D7150FCBFA7C1.avi,51.86474412,18.1522931,54.03930391,17.42386913,46
151
- 0X352D7150FCBFA7C1.avi,51.25048236,20.47771263,57.77416174,18.29244071,46
152
- 0X352D7150FCBFA7C1.avi,50.63622059,22.80313216,60.71332962,19.42754884,46
153
- 0X352D7150FCBFA7C1.avi,50.02195883,25.12855169,62.4651479,20.96038983,46
154
- 0X352D7150FCBFA7C1.avi,49.58600989,27.39424081,64.21696618,22.49323082,46
155
- 0X352D7150FCBFA7C1.avi,49.48817215,29.54667101,65.96878446,24.02607182,46
156
- 0X352D7150FCBFA7C1.avi,49.39033441,31.69910121,67.72060274,25.55891281,46
157
- 0X352D7150FCBFA7C1.avi,49.29249668,33.85153141,69.13705572,27.20409292,46
158
- 0X352D7150FCBFA7C1.avi,49.25520833,35.98367906,70.06373992,29.0133335,46
159
- 0X352D7150FCBFA7C1.avi,49.25520833,38.10333603,70.99042413,30.82257409,46
160
- 0X352D7150FCBFA7C1.avi,49.25520833,40.222993,71.91710833,32.63181467,46
161
- 0X352D7150FCBFA7C1.avi,49.25520833,42.34264996,72.81011857,34.45233521,46
162
- 0X352D7150FCBFA7C1.avi,49.25520833,44.46230693,73.61454998,36.30252746,46
163
- 0X352D7150FCBFA7C1.avi,49.25520833,46.5819639,74.4189814,38.15271972,46
164
- 0X352D7150FCBFA7C1.avi,49.41717731,48.64736525,75.22341282,40.00291198,46
165
- 0X352D7150FCBFA7C1.avi,50.0527613,50.55411724,76.02784423,41.85310424,46
166
- 0X352D7150FCBFA7C1.avi,50.6883453,52.46086922,76.36473964,43.85990954,46
167
- 0X352D7150FCBFA7C1.avi,51.32392929,54.36762121,76.54013569,45.92081315,46
168
- 0X352D7150FCBFA7C1.avi,51.95951329,56.27437319,76.71553175,47.98171676,46
169
- 0X352D7150FCBFA7C1.avi,66.33136696,53.57981317,76.8909278,50.04262037,46
170
  0X36B33D2B6530F83E.avi,47.61458333,12.796875,51.625,57.82291667,106
171
  0X36B33D2B6530F83E.avi,47.03179306,14.01795939,49.25192682,13.82021468,106
172
  0X36B33D2B6530F83E.avi,45.8662125,16.46012817,52.57323857,15.86274123,106
@@ -503,48 +461,6 @@ FileName,X1,Y1,X2,Y2,Frame
503
  0X623F4667A42B8338.avi,51.15840311,63.83187801,70.09225933,58.82733452,47
504
  0X623F4667A42B8338.avi,51.40727935,66.07176411,70.21849828,61.09963576,47
505
  0X623F4667A42B8338.avi,52.82458486,68.00281428,59.16708248,66.32638319,47
506
- 0X6382AE8EB4B73A3D.avi,42.69270833,32.11979167,64.75,79.88020833,105
507
- 0X6382AE8EB4B73A3D.avi,42.75418306,33.59518516,45.04089118,32.53911003,105
508
- 0X6382AE8EB4B73A3D.avi,42.87704877,36.54601082,49.10540384,33.66955675,105
509
- 0X6382AE8EB4B73A3D.avi,42.99516945,39.49902788,52.27247629,35.21447014,105
510
- 0X6382AE8EB4B73A3D.avi,43.2041047,42.41010386,55.39453828,36.78017079,105
511
- 0X6382AE8EB4B73A3D.avi,43.51378561,45.27465227,57.71668879,38.71529622,105
512
- 0X6382AE8EB4B73A3D.avi,43.84747576,48.12811244,60.0388393,40.65042164,105
513
- 0X6382AE8EB4B73A3D.avi,44.3580637,50.89987554,62.07196445,42.71902825,105
514
- 0X6382AE8EB4B73A3D.avi,44.86865164,53.67163864,63.9801778,44.84532312,105
515
- 0X6382AE8EB4B73A3D.avi,45.79554623,56.25113798,65.88839115,46.971618,105
516
- 0X6382AE8EB4B73A3D.avi,46.97722542,58.71296963,67.57281822,49.20126455,105
517
- 0X6382AE8EB4B73A3D.avi,48.15890461,61.17480127,68.85008614,51.61895026,105
518
- 0X6382AE8EB4B73A3D.avi,49.3405838,63.63663292,70.12735406,54.03663596,105
519
- 0X6382AE8EB4B73A3D.avi,49.98025454,66.34878144,71.40462198,56.45432167,105
520
- 0X6382AE8EB4B73A3D.avi,50.57150052,69.08329407,72.59497827,58.91214595,105
521
- 0X6382AE8EB4B73A3D.avi,50.84050148,71.96662979,73.62983127,61.44178663,105
522
- 0X6382AE8EB4B73A3D.avi,50.7179345,75.03080412,74.66468428,63.97142732,105
523
- 0X6382AE8EB4B73A3D.avi,50.97807563,77.91823159,75.55919254,66.56588371,105
524
- 0X6382AE8EB4B73A3D.avi,52.01791822,80.44556792,75.84667268,69.44068514,105
525
- 0X6382AE8EB4B73A3D.avi,55.49621283,81.84674894,75.95152344,72.39983068,105
526
- 0X6382AE8EB4B73A3D.avi,67.64628257,79.24302617,75.59927352,75.57007996,105
527
- 0X6382AE8EB4B73A3D.avi,49.61979167,33.03125,64.203125,76.96354167,90
528
- 0X6382AE8EB4B73A3D.avi,49.48283763,34.30948768,50.85427487,33.8542388,90
529
- 0X6382AE8EB4B73A3D.avi,49.20892955,36.86596305,53.32324129,35.50021641,90
530
- 0X6382AE8EB4B73A3D.avi,49.14252472,39.35355766,55.35684419,37.29071303,90
531
- 0X6382AE8EB4B73A3D.avi,49.28079067,41.77321178,56.96626433,39.2220172,90
532
- 0X6382AE8EB4B73A3D.avi,49.41905662,44.19286589,58.57568448,41.15332137,90
533
- 0X6382AE8EB4B73A3D.avi,49.57686062,46.60603434,60.18510462,43.08462554,90
534
- 0X6382AE8EB4B73A3D.avi,49.73767197,49.0182045,61.54893279,45.09745402,90
535
- 0X6382AE8EB4B73A3D.avi,49.89848331,51.43037466,62.70540985,47.17911274,90
536
- 0X6382AE8EB4B73A3D.avi,50.01643199,53.8567731,63.86188692,49.26077146,90
537
- 0X6382AE8EB4B73A3D.avi,50.08524073,56.29948354,65.01836399,51.34243018,90
538
- 0X6382AE8EB4B73A3D.avi,50.15404948,58.74219398,66.04575691,53.46693841,90
539
- 0X6382AE8EB4B73A3D.avi,50.22285822,61.18490443,66.98764672,55.61982941,90
540
- 0X6382AE8EB4B73A3D.avi,50.29166697,63.62761487,67.92953653,57.77272041,90
541
- 0X6382AE8EB4B73A3D.avi,50.36433888,66.06904294,68.87142635,59.92561141,90
542
- 0X6382AE8EB4B73A3D.avi,50.45622753,68.504092,69.61306673,62.14497526,90
543
- 0X6382AE8EB4B73A3D.avi,50.54811617,70.93914107,70.18222182,64.42159563,90
544
- 0X6382AE8EB4B73A3D.avi,50.64000482,73.37419014,70.75137692,66.698216,90
545
- 0X6382AE8EB4B73A3D.avi,51.00395115,75.7189296,71.17584133,69.02286647,90
546
- 0X6382AE8EB4B73A3D.avi,52.45805061,77.7017925,71.52398717,71.3728509,90
547
- 0X6382AE8EB4B73A3D.avi,57.28586759,78.56474918,71.19545567,73.94745853,90
548
  0X63FECBA90BA79F81.avi,57.16666667,28,66.03333333,95.66666667,151
549
  0X63FECBA90BA79F81.avi,52.8280287,30.354879,62.43932692,29.09546751,151
550
  0X63FECBA90BA79F81.avi,49.80404232,34.32386112,67.42054487,32.01549182,151
 
125
  0X34D6A77674B8E6F2.avi,49.34517087,68.20596855,71.0245205,62.02791555,97
126
  0X34D6A77674B8E6F2.avi,49.63605055,70.06759853,71.56193286,63.8192901,97
127
  0X34D6A77674B8E6F2.avi,59.82206211,69.10937228,71.56568191,65.76274487,97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  0X36B33D2B6530F83E.avi,47.61458333,12.796875,51.625,57.82291667,106
129
  0X36B33D2B6530F83E.avi,47.03179306,14.01795939,49.25192682,13.82021468,106
130
  0X36B33D2B6530F83E.avi,45.8662125,16.46012817,52.57323857,15.86274123,106
 
461
  0X623F4667A42B8338.avi,51.15840311,63.83187801,70.09225933,58.82733452,47
462
  0X623F4667A42B8338.avi,51.40727935,66.07176411,70.21849828,61.09963576,47
463
  0X623F4667A42B8338.avi,52.82458486,68.00281428,59.16708248,66.32638319,47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  0X63FECBA90BA79F81.avi,57.16666667,28,66.03333333,95.66666667,151
465
  0X63FECBA90BA79F81.avi,52.8280287,30.354879,62.43932692,29.09546751,151
466
  0X63FECBA90BA79F81.avi,49.80404232,34.32386112,67.42054487,32.01549182,151
app.py CHANGED
@@ -20,6 +20,7 @@ import pandas as pd
20
  from scipy.integrate import odeint
21
  import torchvision
22
  import echonet
 
23
 
24
  device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
25
 
@@ -455,9 +456,10 @@ def pvloop_simulator(Rm, Ra, Emax, Emin, Vd, Tc, start_v):
455
 
456
  # Create the figure and the loop that we will manipulate
457
  fig, ax = plt.subplots()
458
- plt.ylim((0,250))
459
- plt.xlim((0,200))
460
- line = ax.plot(volumes[(N-2)*60000:(N)*60000], pressures[(N-2)*60000:(N)*60000], lw=1)
 
461
  #print(line)
462
  line = line[0]
463
  #print(line)
@@ -469,7 +471,17 @@ def pvloop_simulator(Rm, Ra, Emax, Emin, Vd, Tc, start_v):
469
  ax.set_ylabel('LV Pressure (mmHg)')
470
 
471
  # adjust the main plot to make room for the sliders
472
- fig.subplots_adjust(left=0.25, bottom=0.25)
 
 
 
 
 
 
 
 
 
 
473
 
474
  return plt, Rm, Ra, Emax, Emin, Vd, Tc, start_v
475
 
 
20
  from scipy.integrate import odeint
21
  import torchvision
22
  import echonet
23
+ import matplotlib.animation as animation
24
 
25
  device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
26
 
 
456
 
457
  # Create the figure and the loop that we will manipulate
458
  fig, ax = plt.subplots()
459
+ plt.ylim((0,220))
460
+ plt.xlim((0,250))
461
+ line = ax.plot(volumes[(N-2)*60000], pressures[(N-2)*60000], lw=1)
462
+ #line = ax.plot(volumes[(N-2)*60000:(N)*60000], pressures[(N-2)*60000:(N)*60000], lw=1)
463
  #print(line)
464
  line = line[0]
465
  #print(line)
 
471
  ax.set_ylabel('LV Pressure (mmHg)')
472
 
473
  # adjust the main plot to make room for the sliders
474
+ # fig.subplots_adjust(left=0.25, bottom=0.25)
475
+
476
+ def update(frame):
477
+ # for each frame, update the data stored on each artist.
478
+ x = volumes[:(N-2)*60000+frame]
479
+ y = pressures[:(N-2)*60000:(N)+frame]
480
+ line.set_xdata(x)
481
+ line.set_ydata(y)
482
+ return line
483
+
484
+ anim = animation.FuncAnimation(fig=fig, func=update, frames=40, interval=30)
485
 
486
  return plt, Rm, Ra, Emax, Emin, Vd, Tc, start_v
487