• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Pablo1990 / pyVertexModel / 11799809301

12 Nov 2024 02:54PM UTC coverage: 0.793% (-0.004%) from 0.797%
11799809301

push

github

Pablo1990
remodelling working ish

0 of 1001 branches covered (0.0%)

Branch coverage included in aggregate %.

0 of 11 new or added lines in 1 file covered. (0.0%)

108 existing lines in 1 file now uncovered.

51 of 5429 relevant lines covered (0.94%)

0.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/pyVertexModel/analysis/analyse_simulation.py
1
import os
×
2
import pickle
×
UNCOV
3
import cv2
×
4

5
import numpy as np
×
6
import pandas as pd
×
7
from matplotlib import pyplot as plt
×
UNCOV
8
from scipy.optimize import curve_fit
×
9

10
from src.pyVertexModel.algorithm.vertexModel import VertexModel, logger
×
UNCOV
11
from src.pyVertexModel.util.utils import load_state, load_variables, save_variables
×
12

13

UNCOV
14
def analyse_simulation(folder):
×
15
    """
16
    Analyse the simulation results
17
    :param folder:
18
    :return:
19
    """
20

21
    # Check if the pkl file exists
UNCOV
22
    if not os.path.exists(os.path.join(folder, 'features_per_time.pkl')):
×
23
        # return None, None, None, None
UNCOV
24
        vModel = VertexModel(create_output_folder=False)
×
25

26
        features_per_time = []
×
UNCOV
27
        features_per_time_all_cells = []
×
28

29
        # Go through all the files in the folder
UNCOV
30
        all_files = os.listdir(folder)
×
31

32
        # Sort files by date
UNCOV
33
        all_files = sorted(all_files, key=lambda x: os.path.getmtime(os.path.join(folder, x)))
×
34

35
        # if all_files has less than 20 files, return None
36
        if len(all_files) < 20:
×
UNCOV
37
            return None, None, None, None
×
38

39
        for file_id, file in enumerate(all_files):
×
UNCOV
40
            if file.endswith('.pkl') and not file.__contains__('data_step_before_remodelling') and not file.__contains__('recoil'):
×
41
                # Load the state of the model
UNCOV
42
                load_state(vModel, os.path.join(folder, file))
×
43

44
                # Analyse the simulation
45
                all_cells, avg_cells = vModel.analyse_vertex_model()
×
46
                features_per_time_all_cells.append(all_cells)
×
UNCOV
47
                features_per_time.append(avg_cells)
×
48

49
                # # Create a temporary directory to store the images
50
                # temp_dir = os.path.join(folder, 'images')
51
                # if not os.path.exists(temp_dir):
52
                #     os.mkdir(temp_dir)
53
                # vModel.screenshot(temp_dir)
54

55
                # temp_dir = os.path.join(folder, 'images_wound_edge')
56
                # if not os.path.exists(temp_dir):
57
                #     os.mkdir(temp_dir)
58
                # _, debris_cells = vModel.geo.compute_wound_centre()
59
                # list_of_cell_distances_top = vModel.geo.compute_cell_distance_to_wound(debris_cells, location_filter=0)
60
                # alive_cells = [cell.ID for cell in vModel.geo.Cells if cell.AliveStatus == 1]
61
                # wound_edge_cells = []
62
                # for cell_num, cell_id in enumerate(alive_cells):
63
                #     if list_of_cell_distances_top[cell_num] == 1:
64
                #         wound_edge_cells.append(cell_id)
65
                # vModel.screenshot(temp_dir, wound_edge_cells)
66

67
        if not features_per_time:
×
UNCOV
68
            return None, None, None, None
×
69

70
        # Export to xlsx
UNCOV
71
        features_per_time_all_cells_df = pd.DataFrame(np.concatenate(features_per_time_all_cells),
×
72
                                                      columns=features_per_time_all_cells[0].columns)
73
        features_per_time_all_cells_df.sort_values(by='time', inplace=True)
×
UNCOV
74
        features_per_time_all_cells_df.to_excel(os.path.join(folder, 'features_per_time_all_cells.xlsx'))
×
75

76
        features_per_time_df = pd.DataFrame(features_per_time)
×
77
        features_per_time_df.sort_values(by='time', inplace=True)
×
UNCOV
78
        features_per_time_df.to_excel(os.path.join(folder, 'features_per_time.xlsx'))
×
79

80
        # Obtain pre-wound features
81
        try:
×
82
            pre_wound_features = features_per_time_df['time'][features_per_time_df['time'] < vModel.set.TInitAblation]
×
UNCOV
83
            pre_wound_features = features_per_time_df[features_per_time_df['time'] ==
×
84
                                                      pre_wound_features.iloc[-1]]
85
        except Exception as e:
×
UNCOV
86
            pre_wound_features = features_per_time_df.iloc[0]
×
87

88
        # Obtain post-wound features
UNCOV
89
        post_wound_features = features_per_time_df[features_per_time_df['time'] >= vModel.set.TInitAblation]
×
90

UNCOV
91
        if not post_wound_features.empty:
×
92
            # Reset time to ablation time.
UNCOV
93
            post_wound_features.loc[:, 'time'] = post_wound_features['time'] - vModel.set.TInitAblation
×
94

95
            # Compare post-wound features with pre-wound features in percentage
96
            for feature in post_wound_features.columns:
×
97
                if np.any(np.isnan(pre_wound_features[feature])) or np.any(np.isnan(post_wound_features[feature])):
×
UNCOV
98
                    continue
×
99

100
                if feature == 'time':
×
101
                    continue
×
UNCOV
102
                post_wound_features.loc[:, feature] = (post_wound_features[feature] / np.array(
×
103
                    pre_wound_features[feature])) * 100
104

105
            # Export to xlsx
UNCOV
106
            post_wound_features.to_excel(os.path.join(folder, 'post_wound_features.xlsx'))
×
107

UNCOV
108
            important_features = calculate_important_features(post_wound_features)
×
109
        else:
UNCOV
110
            important_features = {
×
111
                'max_recoiling_top': np.nan,
112
                'max_recoiling_time_top': np.nan,
113
                'min_height_change': np.nan,
114
                'min_height_change_time': np.nan,
115
            }
116

117
        # Export to xlsx
118
        df = pd.DataFrame([important_features])
×
UNCOV
119
        df.to_excel(os.path.join(folder, 'important_features.xlsx'))
×
120

121
        # Save dataframes to a single pkl
122
        with open(os.path.join(folder, 'features_per_time.pkl'), 'wb') as f:
×
123
            pickle.dump(features_per_time_df, f)
×
124
            pickle.dump(important_features, f)
×
125
            pickle.dump(post_wound_features, f)
×
UNCOV
126
            pickle.dump(features_per_time_all_cells_df, f)
×
127

128
    else:
129
        # Load dataframes from pkl
130
        with open(os.path.join(folder, 'features_per_time.pkl'), 'rb') as f:
×
131
            features_per_time_df = pickle.load(f)
×
132
            important_features = pickle.load(f)
×
133
            post_wound_features = pickle.load(f)
×
UNCOV
134
            features_per_time_all_cells_df = pickle.load(f)
×
135

UNCOV
136
        important_features = calculate_important_features(post_wound_features)
×
137

138
    # Plot wound area top evolution over time and save it to a file
139
    plot_feature(folder, post_wound_features, name='wound_area_top')
×
140
    plot_feature(folder, post_wound_features, name='wound_height')
×
UNCOV
141
    plot_feature(folder, post_wound_features, name='num_cells_wound_edge_top')
×
142

UNCOV
143
    return features_per_time_df, post_wound_features, important_features, features_per_time_all_cells_df
×
144

145

UNCOV
146
def plot_feature(folder, post_wound_features, name='wound_area_top'):
×
147
    """
148
    Plot a feature and save it to a file
149
    :param folder:
150
    :param post_wound_features:
151
    :param name:
152
    :return:
153
    """
154
    plt.figure()
×
155
    plt.plot(post_wound_features['time'], post_wound_features[name])
×
156
    plt.xlabel('Time (h)')
×
UNCOV
157
    plt.ylabel(name)
×
158
    # Change axis limits
159
    if np.max(post_wound_features['time']) > 60:
×
UNCOV
160
        plt.xlim([0, np.max(post_wound_features['time'])])
×
161
    else:
162
        plt.xlim([0, 60])
×
163
    plt.ylim([0, 250])
×
164
    plt.savefig(os.path.join(folder, name + '.png'))
×
UNCOV
165
    plt.close()
×
166

167

UNCOV
168
def calculate_important_features(post_wound_features):
×
169
    """
170
    Calculate important features from the post-wound features
171
    :param post_wound_features:
172
    :return:
173
    """
174
    # Obtain important features for post-wound
175
    if not post_wound_features['wound_area_top'].empty and post_wound_features['time'].iloc[-1] > 4:
×
UNCOV
176
        important_features = {
×
177
            'max_recoiling_top': np.max(post_wound_features['wound_area_top']),
178
            'max_recoiling_time_top': np.array(post_wound_features['time'])[
179
                np.argmax(post_wound_features['wound_area_top'])],
180
            'min_recoiling_top': np.min(post_wound_features['wound_area_top']),
181
            'min_recoiling_time_top': np.array(post_wound_features['time'])[
182
                np.argmin(post_wound_features['wound_area_top'])],
183
            'min_height_change': np.min(post_wound_features['wound_height']),
184
            'min_height_change_time': np.array(post_wound_features['time'])[
185
                np.argmin(post_wound_features['wound_height'])],
186
            'last_area_top': post_wound_features['wound_area_top'].iloc[-1],
187
            'last_area_time_top': post_wound_features['time'].iloc[-1],
188
        }
189

190
        # Extrapolate features to a given time
191
        times_to_extrapolate = {3.0, 6.0, 9.0, 12.0, 15.0, 21.0, 30.0, 36.0, 45.0, 51.0, 60.0}
×
192
        columns_to_extrapolate = {'wound_area_top', 'wound_height'}  # post_wound_features.columns
×
193
        for feature in columns_to_extrapolate:
×
UNCOV
194
            for time in times_to_extrapolate:
×
195
                # Extrapolate results to a given time
UNCOV
196
                important_features[feature + '_extrapolated_' + str(time)] = np.interp(time,
×
197
                                                                                       post_wound_features['time'],
198
                                                                                       post_wound_features[feature])
199

200
        # # Get ratio from area the first time to the other times
201
        # for time in times_to_extrapolate:
202
        #     if time != 6.0:
203
        #         important_features['ratio_area_top_' + str(time)] = (
204
        #                 important_features['wound_area_top_extrapolated_' + str(time)] /
205
        #                 important_features['wound_area_top_extrapolated_6.0'])
206

207
    else:
UNCOV
208
        important_features = {
×
209
            'max_recoiling_top': np.nan,
210
            'max_recoiling_time_top': np.nan,
211
            'min_height_change': np.nan,
212
            'min_height_change_time': np.nan,
213
        }
214

UNCOV
215
    return important_features
×
216

217

UNCOV
218
def analyse_edge_recoil(file_name_v_model, type_of_ablation='recoil_edge_info_apical', n_ablations=2, location_filter=0, t_end=0.5):
×
219
    """
220
    Analyse how much an edge recoil if we ablate an edge of a cell
221
    :param type_of_ablation:
222
    :param t_end: Time to iterate after the ablation
223
    :param file_name_v_model: file nae of the Vertex model
224
    :param n_ablations: Number of ablations to perform
225
    :param location_filter: Location filter
226
    :return:
227
    """
228

229
    v_model = VertexModel(create_output_folder=False)
×
UNCOV
230
    load_state(v_model, file_name_v_model)
×
231

232
    # Cells to ablate
233
    # cell_to_ablate = np.random.choice(possible_cells_to_ablate, 1)
UNCOV
234
    cell_to_ablate = [v_model.geo.Cells[0]]
×
235

236
    #Pick the neighbouring cell to ablate
UNCOV
237
    neighbours = cell_to_ablate[0].compute_neighbours(location_filter)
×
238

239
    # Random order of neighbours
240
    np.random.seed(0)
×
UNCOV
241
    np.random.shuffle(neighbours)
×
242

243
    list_of_dicts_to_save = []
×
244
    for num_ablation in range(n_ablations):
×
245
        load_state(v_model, file_name_v_model)
×
246
        try:
×
247
            vars = load_variables(file_name_v_model.replace('before_ablation.pkl', type_of_ablation + '.pkl'))
×
UNCOV
248
            list_of_dicts_to_save_loaded = vars['recoiling_info_df_apical']
×
249

250
            cell_to_ablate_ID = list_of_dicts_to_save_loaded['cell_to_ablate'][num_ablation]
×
251
            neighbour_to_ablate_ID = list_of_dicts_to_save_loaded['neighbour_to_ablate'][num_ablation]
×
252
            edge_length_init = list_of_dicts_to_save_loaded['edge_length_init'][num_ablation]
×
253
            edge_length_final = list_of_dicts_to_save_loaded['edge_length_final'][num_ablation]
×
254
            if 'edge_length_final_normalized' in list_of_dicts_to_save_loaded:
×
UNCOV
255
                edge_length_final_normalized = list_of_dicts_to_save_loaded['edge_length_final_normalized'][
×
256
                    num_ablation]
257
            else:
UNCOV
258
                edge_length_final_normalized = (edge_length_final - edge_length_init) / edge_length_init
×
259

260
            initial_recoil = list_of_dicts_to_save_loaded['initial_recoil_in_s'][num_ablation]
×
261
            K = list_of_dicts_to_save_loaded['K'][num_ablation]
×
262
            scutoid_face = list_of_dicts_to_save_loaded['scutoid_face'][num_ablation]
×
263
            distance_to_centre = list_of_dicts_to_save_loaded['distance_to_centre'][num_ablation]
×
264
            if 'time_steps' in list_of_dicts_to_save_loaded:
×
UNCOV
265
                time_steps = list_of_dicts_to_save_loaded['time_steps'][num_ablation]
×
266
            else:
UNCOV
267
                time_steps = np.arange(0, len(edge_length_final)) * 6
×
268

UNCOV
269
            if edge_length_final[0] == 0:
×
270
                # Remove the first element
271
                edge_length_final = edge_length_final[1:]
×
272
                time_steps = time_steps[1:]
×
273
        except Exception as e:
×
UNCOV
274
            logger.info('Performing the analysis...' + str(e))
×
275
            # Change name of folder and create it
276
            if type_of_ablation == 'recoil_info_apical':
×
UNCOV
277
                v_model.set.OutputFolder = v_model.set.OutputFolder + '_ablation_' + str(num_ablation)
×
278
            else:
UNCOV
279
                v_model.set.OutputFolder = v_model.set.OutputFolder + '_ablation_edge_' + str(num_ablation)
×
280

281
            if not os.path.exists(v_model.set.OutputFolder):
×
UNCOV
282
                os.mkdir(v_model.set.OutputFolder)
×
283

UNCOV
284
            neighbour_to_ablate = [neighbours[num_ablation]]
×
285

286
            # Calculate if the cell is neighbour on both sides
287
            scutoid_face = None
×
288
            neighbours_other_side = []
×
289
            if location_filter == 0:
×
290
                neighbours_other_side = cell_to_ablate[0].compute_neighbours(location_filter=2)
×
291
                scutoid_face = np.nan
×
292
            elif location_filter == 2:
×
293
                neighbours_other_side = cell_to_ablate[0].compute_neighbours(location_filter=0)
×
UNCOV
294
                scutoid_face = np.nan
×
295

296
            if scutoid_face is not None:
×
297
                if neighbour_to_ablate[0] in neighbours_other_side:
×
UNCOV
298
                    scutoid_face = True
×
299
                else:
UNCOV
300
                    scutoid_face = False
×
301

302
            # Get the centre of the tissue
303
            centre_of_tissue = v_model.geo.compute_centre_of_tissue()
×
304
            neighbour_to_ablate_cell = [cell for cell in v_model.geo.Cells if cell.ID == neighbour_to_ablate[0]][0]
×
UNCOV
305
            distance_to_centre = np.mean([cell_to_ablate[0].compute_distance_to_centre(centre_of_tissue),
×
306
                                          neighbour_to_ablate_cell.compute_distance_to_centre(centre_of_tissue)])
307

308
            # Pick the neighbour and put it in the list
UNCOV
309
            cells_to_ablate = [cell_to_ablate[0].ID, neighbour_to_ablate[0]]
×
310

311
            # Get the edge that share both cells
UNCOV
312
            edge_length_init = v_model.geo.get_edge_length(cells_to_ablate, location_filter)
×
313

314
            # Ablate the edge
315
            v_model.set.ablation = True
×
316
            v_model.geo.cellsToAblate = cells_to_ablate
×
317
            v_model.set.TInitAblation = v_model.t
×
318
            if type_of_ablation == 'recoil_info_apical':
×
319
                v_model.geo.ablate_cells(v_model.set, v_model.t, combine_cells=False)
×
320
                v_model.geo.y_ablated = []
×
321
            elif type_of_ablation == 'recoil_edge_info_apical':
×
UNCOV
322
                v_model.geo.y_ablated = v_model.geo.ablate_edge(v_model.set, v_model.t, domain=location_filter,
×
323
                                                                adjacent_surface=False)
324

325
            # Relax the system
326
            initial_time = v_model.t
×
327
            v_model.set.tend = v_model.t + t_end
×
328
            if type_of_ablation == 'recoil_info_apical':
×
329
                v_model.set.dt = 0.005
×
330
            elif type_of_ablation == 'recoil_edge_info_apical':
×
UNCOV
331
                v_model.set.dt = 0.005
×
332

UNCOV
333
            v_model.set.Remodelling = False
×
334

335
            v_model.set.dt0 = v_model.set.dt
×
336
            if type_of_ablation == 'recoil_edge_info_apical':
×
UNCOV
337
                v_model.set.RemodelingFrequency = 0.05
×
338
            else:
339
                v_model.set.RemodelingFrequency = 100
×
340
            v_model.set.ablation = False
×
341
            v_model.set.export_images = True
×
342
            if v_model.set.export_images and not os.path.exists(v_model.set.OutputFolder + '/images'):
×
343
                os.mkdir(v_model.set.OutputFolder + '/images')
×
344
            edge_length_final_normalized = []
×
345
            edge_length_final = []
×
346
            recoil_speed = []
×
UNCOV
347
            time_steps = []
×
348

349
            # if os.path.exists(v_model.set.OutputFolder):
350
            #     list_of_files = os.listdir(v_model.set.OutputFolder)
351
            #     # Get file modification times and sort files by date
352
            #     files_with_dates = [(file, os.path.getmtime(os.path.join(v_model.set.OutputFolder, file))) for file in
353
            #                         list_of_files]
354
            #     files_with_dates.sort(key=lambda x: x[1])
355
            #     for file in files_with_dates:
356
            #         load_state(v_model, os.path.join(v_model.set.OutputFolder, file[0]))
357
            #         compute_edge_length_v_model(cells_to_ablate, edge_length_final, edge_length_final_normalized,
358
            #                                     edge_length_init, initial_time, location_filter, recoil_speed,
359
            #                                     time_steps,
360
            #                                     v_model)
361

362
            while v_model.t <= v_model.set.tend and not v_model.didNotConverge:
×
UNCOV
363
                gr = v_model.single_iteration()
×
364

UNCOV
365
                compute_edge_length_v_model(cells_to_ablate, edge_length_final, edge_length_final_normalized,
×
366
                                            edge_length_init, initial_time, location_filter, recoil_speed, time_steps,
367
                                            v_model)
368

369
                if np.isnan(gr):
×
UNCOV
370
                    break
×
371

372
            cell_to_ablate_ID = cell_to_ablate[0].ID
×
UNCOV
373
            neighbour_to_ablate_ID = neighbour_to_ablate[0]
×
374

UNCOV
375
        K, initial_recoil, error_bars = fit_ablation_equation(edge_length_final, time_steps)
×
376

377
        # Generate a plot with the edge length final and the fit for each ablation
378
        plt.figure()
×
UNCOV
379
        plt.plot(time_steps, edge_length_final_normalized, 'o')
×
380
        # Plot fit line of the Kelvin-Voigt model
381
        plt.plot(time_steps, recoil_model(np.array(time_steps), initial_recoil, K), 'r')
×
382
        plt.xlabel('Time (s)')
×
383
        plt.ylabel('Edge length final')
×
UNCOV
384
        plt.title('Ablation fit - ' + str(cell_to_ablate_ID) + ' ' + str(neighbour_to_ablate_ID))
×
385

386
        # Save plot
387
        if type_of_ablation == 'recoil_info_apical':
×
UNCOV
388
            plt.savefig(
×
389
                os.path.join(file_name_v_model.replace('before_ablation.pkl', 'ablation_fit_' + str(num_ablation) + '.png'))
390
            )
391
        elif type_of_ablation == 'recoil_edge_info_apical':
×
UNCOV
392
            plt.savefig(
×
393
                os.path.join(file_name_v_model.replace('before_ablation.pkl', 'ablation_edge_fit_' + str(num_ablation) + '.png'))
394
            )
UNCOV
395
        plt.close()
×
396

397
        # Save the results
UNCOV
398
        dict_to_save = {
×
399
            'cell_to_ablate': cell_to_ablate_ID,
400
            'neighbour_to_ablate': neighbour_to_ablate_ID,
401
            'edge_length_init': edge_length_init,
402
            'edge_length_final': edge_length_final,
403
            'edge_length_final_normalized': edge_length_final_normalized,
404
            'initial_recoil_in_s': initial_recoil,
405
            'K': K,
406
            'scutoid_face': scutoid_face,
407
            'location_filter': location_filter,
408
            'distance_to_centre': distance_to_centre,
409
            'time_steps': time_steps,
410
        }
UNCOV
411
        list_of_dicts_to_save.append(dict_to_save)
×
412

413
    recoiling_info_df_apical = pd.DataFrame(list_of_dicts_to_save)
×
414
    recoiling_info_df_apical.to_excel(file_name_v_model.replace('before_ablation.pkl', type_of_ablation+'.xlsx'))
×
UNCOV
415
    save_variables({'recoiling_info_df_apical': recoiling_info_df_apical},
×
416
                   file_name_v_model.replace('before_ablation.pkl', type_of_ablation+'.pkl'))
417

UNCOV
418
    return list_of_dicts_to_save
×
419

420

UNCOV
421
def recoil_model(x, initial_recoil, K):
×
422
    """
423
    Model of the recoil based on a Kelvin-Voigt model
424
    :param x:
425
    :param initial_recoil:
426
    :param K:
427
    :return:   Recoil
428
    """
UNCOV
429
    return (initial_recoil / K) * (1 - np.exp(-K * x))
×
430

431

UNCOV
432
def fit_ablation_equation(edge_length_final_normalized, time_steps):
×
433
    """
434
    Fit the ablation equation. Thanks to Veronika Lachina.
435
    :param edge_length_final_normalized:
436
    :param time_steps:
437
    :return:    K, initial_recoil
438
    """
439

440
    # Normalize the edge length
441
    edge_length_init = edge_length_final_normalized[0]
×
UNCOV
442
    edge_length_final_normalized = (edge_length_final_normalized - edge_length_init) / edge_length_init
×
443

444
    # Fit the model to the data
UNCOV
445
    [params, covariance] = curve_fit(recoil_model, time_steps, edge_length_final_normalized,
×
446
                                     p0=[0.00001, 3], bounds=(0, np.inf))
447

448
    # Get the error
UNCOV
449
    error_bars = np.sqrt(np.diag(covariance))
×
450

451
    initial_recoil, K = params
×
UNCOV
452
    return K, initial_recoil, error_bars
×
453

454

UNCOV
455
def compute_edge_length_v_model(cells_to_ablate, edge_length_final, edge_length_final_normalized, edge_length_init,
×
456
                                initial_time, location_filter, recoil_speed, time_steps, v_model):
457
    """
458
    Compute the edge length of the edge that share the cells_to_ablate
459
    :param cells_to_ablate:
460
    :param edge_length_final:
461
    :param edge_length_final_normalized:
462
    :param edge_length_init:
463
    :param initial_time:
464
    :param location_filter:
465
    :param recoil_speed:
466
    :param time_steps:
467
    :param v_model:
468
    :return:
469
    """
470
    if v_model.t == initial_time:
×
UNCOV
471
        return
×
472
    # Get the edge length
473
    edge_length_final.append(v_model.geo.get_edge_length(cells_to_ablate, location_filter))
×
474
    edge_length_final_normalized.append((edge_length_final[-1] - edge_length_init) / edge_length_init)
×
UNCOV
475
    print('Edge length final: ', edge_length_final[-1])
×
476
    # In seconds. 1 t = 1 minute = 60 seconds
UNCOV
477
    time_steps.append((v_model.t - initial_time) * 60)
×
478
    # Calculate the recoil
UNCOV
479
    recoil_speed.append(edge_length_final_normalized[-1] / time_steps[-1])
×
480

UNCOV
481
def create_video(folder, name_containing_images='top_'):
×
482
    """
483
    Create a video of the images in the folder
484
    :param folder:
485
    :return:
486
    """
487

488
    # Get the images
UNCOV
489
    images = [img for img in os.listdir(folder) if img.endswith(".png") and name_containing_images in img]
×
UNCOV
490
    images.sort(key=lambda x: int(x.split('_')[-1].split('.')[0]))
×
491

492
    # Determine the width and height from the first image
UNCOV
493
    image_path = os.path.join(folder, images[0])
×
UNCOV
494
    frame = cv2.imread(image_path)
×
UNCOV
495
    height, width, layers = frame.shape
×
496

497
    # Define the codec and create VideoWriter object
UNCOV
498
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')  # Use 'mp4v' for MP4
×
UNCOV
499
    video = cv2.VideoWriter(os.path.join(folder, name_containing_images + 'video.mp4'), fourcc, 7, (width, height))
×
500

UNCOV
501
    for image in images:
×
UNCOV
502
        video.write(cv2.imread(os.path.join(folder, image)))
×
503

UNCOV
504
    cv2.destroyAllWindows()
×
UNCOV
505
    video.release()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc