Daan Odijk commited on
Commit
48017c0
2 Parent(s): 61ebda9 d5b407d

Merge pull request #1 from dodijk/restructure

Browse files
Files changed (2) hide show
  1. app.py +164 -16
  2. clip_data.ipynb +276 -3
app.py CHANGED
@@ -16,13 +16,19 @@ import matplotlib.pyplot as plt
16
  import imagehash
17
  from PIL import Image
18
 
19
- import numpy as np
20
  import pandas as pd
21
  import faiss
22
 
23
  import shutil
24
 
 
 
 
 
25
  FPS = 5
 
 
26
 
27
  video_directory = tempfile.gettempdir()
28
 
@@ -77,6 +83,8 @@ def compute_hashes(clip, fps=FPS):
77
  yield {"frame": 1+index*fps, "hash": hashed}
78
 
79
  def index_hashes_for_video(url, is_file = False):
 
 
80
  if not is_file:
81
  filename = download_video_from_url(url)
82
  else:
@@ -105,7 +113,7 @@ def index_hashes_for_video(url, is_file = False):
105
  logging.info(f"Indexed hashes for {index.ntotal} frames to {filename}.index.")
106
  return index
107
 
108
- def compare_videos(url, target, MIN_DISTANCE = 3): # , is_file = False):
109
  """" The comparison between the target and the original video will be plotted based
110
  on the matches between the target and the original video over time. The matches are determined
111
  based on the minimum distance between hashes (as computed by faiss-vectors) before they're considered a match.
@@ -116,9 +124,8 @@ def compare_videos(url, target, MIN_DISTANCE = 3): # , is_file = False):
116
  - MIN_DISTANCE: integer representing the minimum distance between hashes on bit-level before its considered a match
117
  """
118
  # TODO: Fix crash if no matches are found
119
- if url.endswith('dl=1'):
120
- is_file = False
121
- elif url.endswith('.mp4'):
122
  is_file = True
123
 
124
  # Url (short video)
@@ -128,13 +135,32 @@ def compare_videos(url, target, MIN_DISTANCE = 3): # , is_file = False):
128
 
129
  # Target video (long video)
130
  target_indices = [index_hashes_for_video(x) for x in [target]]
131
-
 
 
 
 
 
132
  # The results are returned as a triplet of 1D arrays
133
  # lims, D, I, where result for query i is in I[lims[i]:lims[i+1]]
134
  # (indices of neighbors), D[lims[i]:lims[i+1]] (distances).
135
  lims, D, I = target_indices[0].range_search(hash_vectors, MIN_DISTANCE)
136
-
137
- return plot_comparison(lims, D, I, hash_vectors, MIN_DISTANCE = MIN_DISTANCE)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  def plot_comparison(lims, D, I, hash_vectors, MIN_DISTANCE = 3):
140
  sns.set_theme()
@@ -168,29 +194,151 @@ def plot_comparison(lims, D, I, hash_vectors, MIN_DISTANCE = 3):
168
  return fig
169
 
170
  logging.basicConfig()
171
- logging.getLogger().setLevel(logging.DEBUG)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
  video_urls = ["https://www.dropbox.com/s/8c89a9aba0w8gjg/Ploumen.mp4?dl=1",
174
  "https://www.dropbox.com/s/rzmicviu1fe740t/Bram%20van%20Ojik%20krijgt%20reprimande.mp4?dl=1",
175
  "https://www.dropbox.com/s/wcot34ldmb84071/Baudet%20ontmaskert%20Omtzigt_%20u%20bent%20door%20de%20mand%20gevallen%21.mp4?dl=1",
 
176
  "https://www.dropbox.com/s/4ognq8lshcujk43/Plenaire_zaal_20200923132426_Omtzigt.mp4?dl=1"]
177
 
178
  index_iface = gr.Interface(fn=lambda url: index_hashes_for_video(url).ntotal,
179
- inputs="text", outputs="text",
 
180
  examples=video_urls, cache_examples=True)
181
 
182
- compare_iface = gr.Interface(fn=compare_videos,
183
- inputs=["text", "text", gr.Slider(1, 25, 3, step=1)], outputs="plot",
 
 
 
 
 
 
184
  examples=[[x, video_urls[-1]] for x in video_urls[:-1]])
185
 
186
- iface = gr.TabbedInterface([index_iface, compare_iface], ["Index", "Compare"])
187
 
188
  if __name__ == "__main__":
189
  import matplotlib
190
- matplotlib.use('SVG')
191
 
192
  logging.basicConfig()
193
- logging.getLogger().setLevel(logging.DEBUG)
194
 
195
- iface.launch()
196
  #iface.launch(auth=("test", "test"), share=True, debug=True)
 
16
  import imagehash
17
  from PIL import Image
18
 
19
+ import numpy as np
20
  import pandas as pd
21
  import faiss
22
 
23
  import shutil
24
 
25
+ from kats.detectors.cusum_detection import CUSUMDetector
26
+ from kats.detectors.robust_stat_detection import RobustStatDetector
27
+ from kats.consts import TimeSeriesData
28
+
29
  FPS = 5
30
+ MIN_DISTANCE = 4
31
+ MAX_DISTANCE = 30
32
 
33
  video_directory = tempfile.gettempdir()
34
 
 
83
  yield {"frame": 1+index*fps, "hash": hashed}
84
 
85
  def index_hashes_for_video(url, is_file = False):
86
+ """ Download a video if it is a url, otherwise refer to the file. Secondly index the video
87
+ using faiss indices and return thi index. """
88
  if not is_file:
89
  filename = download_video_from_url(url)
90
  else:
 
113
  logging.info(f"Indexed hashes for {index.ntotal} frames to {filename}.index.")
114
  return index
115
 
116
+ def get_video_indices(url, target, MIN_DISTANCE = 4):
117
  """" The comparison between the target and the original video will be plotted based
118
  on the matches between the target and the original video over time. The matches are determined
119
  based on the minimum distance between hashes (as computed by faiss-vectors) before they're considered a match.
 
124
  - MIN_DISTANCE: integer representing the minimum distance between hashes on bit-level before its considered a match
125
  """
126
  # TODO: Fix crash if no matches are found
127
+ is_file = False
128
+ if url.endswith('.mp4'):
 
129
  is_file = True
130
 
131
  # Url (short video)
 
135
 
136
  # Target video (long video)
137
  target_indices = [index_hashes_for_video(x) for x in [target]]
138
+
139
+ return video_index, hash_vectors, target_indices
140
+
141
+ def compare_videos(video_index, hash_vectors, target_indices, MIN_DISTANCE = 3): # , is_file = False):
142
+ """ Search for matches between the indices of the target video (long video)
143
+ and the given hash vectors of a video"""
144
  # The results are returned as a triplet of 1D arrays
145
  # lims, D, I, where result for query i is in I[lims[i]:lims[i+1]]
146
  # (indices of neighbors), D[lims[i]:lims[i+1]] (distances).
147
  lims, D, I = target_indices[0].range_search(hash_vectors, MIN_DISTANCE)
148
+ return lims, D, I, hash_vectors
149
+
150
+ def get_decent_distance(url, target, MIN_DISTANCE, MAX_DISTANCE):
151
+ """ To get a decent heurstic for a base distance check every distance from MIN_DISTANCE to MAX_DISTANCE
152
+ until the number of matches found is equal to or higher than the number of frames in the source video"""
153
+ for distance in np.arange(start = MIN_DISTANCE - 2, stop = MAX_DISTANCE + 2, step = 2, dtype=int):
154
+ distance = int(distance)
155
+ video_index, hash_vectors, target_indices = get_video_indices(url, target, MIN_DISTANCE = distance)
156
+ lims, D, I, hash_vectors = compare_videos(video_index, hash_vectors, target_indices, MIN_DISTANCE = distance)
157
+ nr_source_frames = video_index.ntotal
158
+ nr_matches = len(D)
159
+ logging.info(f"{(nr_matches/nr_source_frames) * 100.0:.1f}% of frames have a match for distance '{distance}' ({nr_matches} matches for {nr_source_frames} frames)")
160
+ if nr_matches >= nr_source_frames:
161
+ return distance
162
+ logging.warning(f"No matches found for any distance between {MIN_DISTANCE} and {MAX_DISTANCE}")
163
+ return None
164
 
165
  def plot_comparison(lims, D, I, hash_vectors, MIN_DISTANCE = 3):
166
  sns.set_theme()
 
194
  return fig
195
 
196
  logging.basicConfig()
197
+ logging.getLogger().setLevel(logging.INFO)
198
+
199
+ def plot_multi_comparison(df, change_points):
200
+ """ From the dataframe plot the current set of plots, where the bottom right is most indicative """
201
+ fig, ax_arr = plt.subplots(3, 2, figsize=(12, 6), dpi=100, sharex=True)
202
+ sns.scatterplot(data = df, x='time', y='SOURCE_S', ax=ax_arr[0,0])
203
+ sns.lineplot(data = df, x='time', y='SOURCE_LIP_S', ax=ax_arr[0,1])
204
+ sns.scatterplot(data = df, x='time', y='OFFSET', ax=ax_arr[1,0])
205
+ sns.lineplot(data = df, x='time', y='OFFSET_LIP', ax=ax_arr[1,1])
206
+
207
+ # Plot change point as lines
208
+ sns.lineplot(data = df, x='time', y='OFFSET_LIP', ax=ax_arr[2,1])
209
+ for x in change_points:
210
+ cp_time = x.start_time
211
+ plt.vlines(x=cp_time, ymin=np.min(df['OFFSET_LIP']), ymax=np.max(df['OFFSET_LIP']), colors='red', lw=2)
212
+ rand_y_pos = np.random.uniform(low=np.min(df['OFFSET_LIP']), high=np.max(df['OFFSET_LIP']), size=None)
213
+ plt.text(x=cp_time, y=rand_y_pos, s=str(np.round(x.confidence, 2)), color='r', rotation=-0.0, fontsize=14)
214
+ plt.xticks(rotation=90)
215
+ return fig
216
+
217
+
218
+ def get_videomatch_df(url, target, min_distance=MIN_DISTANCE, vanilla_df=False):
219
+ distance = get_decent_distance(url, target, MIN_DISTANCE, MAX_DISTANCE)
220
+ video_index, hash_vectors, target_indices = get_video_indices(url, target, MIN_DISTANCE = distance)
221
+ lims, D, I, hash_vectors = compare_videos(video_index, hash_vectors, target_indices, MIN_DISTANCE = distance)
222
+
223
+ target = [(lims[i+1]-lims[i]) * [i] for i in range(hash_vectors.shape[0])]
224
+ target_s = [i/FPS for j in target for i in j]
225
+ source_s = [i/FPS for i in I]
226
+
227
+ # Make df
228
+ df = pd.DataFrame(zip(target_s, source_s, D, I), columns = ['TARGET_S', 'SOURCE_S', 'DISTANCE', 'INDICES'])
229
+ if vanilla_df:
230
+ return df
231
+
232
+ # Minimum distance dataframe ----
233
+ # Group by X so for every second/x there will be 1 value of Y in the end
234
+ # index_min_distance = df.groupby('TARGET_S')['DISTANCE'].idxmin()
235
+ # df_min = df.loc[index_min_distance]
236
+ # df_min
237
+ # -------------------------------
238
+
239
+ df['TARGET_WEIGHT'] = 1 - df['DISTANCE']/distance # Higher value means a better match
240
+ df['SOURCE_WEIGHTED_VALUE'] = df['SOURCE_S'] * df['TARGET_WEIGHT'] # Multiply the weight (which indicates a better match) with the value for Y and aggregate to get a less noisy estimate of Y
241
+
242
+ # Group by X so for every second/x there will be 1 value of Y in the end
243
+ grouped_X = df.groupby('TARGET_S').agg({'SOURCE_WEIGHTED_VALUE' : 'sum', 'TARGET_WEIGHT' : 'sum'})
244
+ grouped_X['FINAL_SOURCE_VALUE'] = grouped_X['SOURCE_WEIGHTED_VALUE'] / grouped_X['TARGET_WEIGHT']
245
+
246
+ # Remake the dataframe
247
+ df = grouped_X.reset_index()
248
+ df = df.drop(columns=['SOURCE_WEIGHTED_VALUE', 'TARGET_WEIGHT'])
249
+ df = df.rename({'FINAL_SOURCE_VALUE' : 'SOURCE_S'}, axis='columns')
250
+
251
+ # Add NAN to "missing" x values (base it off hash vector, not target_s)
252
+ step_size = 1/FPS
253
+ x_complete = np.round(np.arange(start=0.0, stop = max(df['TARGET_S'])+step_size, step = step_size), 1) # More robust
254
+ df['TARGET_S'] = np.round(df['TARGET_S'], 1)
255
+ df_complete = pd.DataFrame(x_complete, columns=['TARGET_S'])
256
+
257
+ # Merge dataframes to get NAN values for every missing SOURCE_S
258
+ df = df_complete.merge(df, on='TARGET_S', how='left')
259
+
260
+ # Interpolate between frames since there are missing values
261
+ df['SOURCE_LIP_S'] = df['SOURCE_S'].interpolate(method='linear', limit_direction='both', axis=0)
262
+
263
+ # Add timeshift col and timeshift col with Linearly Interpolated Values
264
+ df['TIMESHIFT'] = df['SOURCE_S'].shift(1) - df['SOURCE_S']
265
+ df['TIMESHIFT_LIP'] = df['SOURCE_LIP_S'].shift(1) - df['SOURCE_LIP_S']
266
+
267
+ # Add Offset col that assumes the video is played at the same speed as the other to do a "timeshift"
268
+ df['OFFSET'] = df['SOURCE_S'] - df['TARGET_S'] - np.min(df['SOURCE_S'])
269
+ df['OFFSET_LIP'] = df['SOURCE_LIP_S'] - df['TARGET_S'] - np.min(df['SOURCE_LIP_S'])
270
+
271
+ # Add time column for plotting
272
+ df['time'] = pd.to_datetime(df["TARGET_S"], unit='s') # Needs a datetime as input
273
+ return df
274
+
275
+ def get_change_points(df, smoothing_window_size=10, method='CUSUM'):
276
+ tsd = TimeSeriesData(df.loc[:,['time','OFFSET_LIP']])
277
+ if method.upper() == "CUSUM":
278
+ detector = CUSUMDetector(tsd)
279
+ elif method.upper() == "ROBUST":
280
+ detector = RobustStatDetector(tsd)
281
+ change_points = detector.detector(smoothing_window_size=smoothing_window_size, comparison_window=-2)
282
+
283
+ # Print some stats
284
+ if method.upper() == "CUSUM" and change_points != []:
285
+ mean_offset_prechange = change_points[0].mu0
286
+ mean_offset_postchange = change_points[0].mu1
287
+ jump_s = mean_offset_postchange - mean_offset_prechange
288
+ print(f"Video jumps {jump_s:.1f}s in time at {mean_offset_prechange:.1f} seconds")
289
+ return change_points
290
+
291
+ def get_comparison(url, target, MIN_DISTANCE = 4):
292
+ """ Function for Gradio to combine all helper functions"""
293
+ video_index, hash_vectors, target_indices = get_video_indices(url, target, MIN_DISTANCE = MIN_DISTANCE)
294
+ lims, D, I, hash_vectors = compare_videos(video_index, hash_vectors, target_indices, MIN_DISTANCE = MIN_DISTANCE)
295
+ fig = plot_comparison(lims, D, I, hash_vectors, MIN_DISTANCE = MIN_DISTANCE)
296
+ return fig
297
+
298
+ def get_auto_comparison(url, target, smoothing_window_size=10, method="CUSUM"):
299
+ """ Function for Gradio to combine all helper functions"""
300
+ distance = get_decent_distance(url, target, MIN_DISTANCE, MAX_DISTANCE)
301
+ if distance == None:
302
+ raise gr.Error("No matches found!")
303
+ video_index, hash_vectors, target_indices = get_video_indices(url, target, MIN_DISTANCE = distance)
304
+ lims, D, I, hash_vectors = compare_videos(video_index, hash_vectors, target_indices, MIN_DISTANCE = distance)
305
+ # fig = plot_comparison(lims, D, I, hash_vectors, MIN_DISTANCE = distance)
306
+ df = get_videomatch_df(url, target, min_distance=MIN_DISTANCE, vanilla_df=False)
307
+ change_points = get_change_points(df, smoothing_window_size=smoothing_window_size, method=method)
308
+ fig = plot_multi_comparison(df, change_points)
309
+ return fig
310
+
311
+
312
 
313
  video_urls = ["https://www.dropbox.com/s/8c89a9aba0w8gjg/Ploumen.mp4?dl=1",
314
  "https://www.dropbox.com/s/rzmicviu1fe740t/Bram%20van%20Ojik%20krijgt%20reprimande.mp4?dl=1",
315
  "https://www.dropbox.com/s/wcot34ldmb84071/Baudet%20ontmaskert%20Omtzigt_%20u%20bent%20door%20de%20mand%20gevallen%21.mp4?dl=1",
316
+ "https://drive.google.com/uc?id=1XW0niHR1k09vPNv1cp6NvdGXe7FHJc1D&export=download",
317
  "https://www.dropbox.com/s/4ognq8lshcujk43/Plenaire_zaal_20200923132426_Omtzigt.mp4?dl=1"]
318
 
319
  index_iface = gr.Interface(fn=lambda url: index_hashes_for_video(url).ntotal,
320
+ inputs="text",
321
+ outputs="text",
322
  examples=video_urls, cache_examples=True)
323
 
324
+ compare_iface = gr.Interface(fn=get_comparison,
325
+ inputs=["text", "text", gr.Slider(2, 30, 4, step=2)],
326
+ outputs="plot",
327
+ examples=[[x, video_urls[-1]] for x in video_urls[:-1]])
328
+
329
+ auto_compare_iface = gr.Interface(fn=get_auto_comparison,
330
+ inputs=["text", "text", gr.Slider(1, 50, 10, step=1), gr.Dropdown(choices=["CUSUM", "Robust"], value="CUSUM")],
331
+ outputs="plot",
332
  examples=[[x, video_urls[-1]] for x in video_urls[:-1]])
333
 
334
+ iface = gr.TabbedInterface([auto_compare_iface, compare_iface, index_iface,], ["AutoCompare", "Compare", "Index"])
335
 
336
  if __name__ == "__main__":
337
  import matplotlib
338
+ matplotlib.use('SVG') # To be able to plot in gradio
339
 
340
  logging.basicConfig()
341
+ logging.getLogger().setLevel(logging.INFO)
342
 
343
+ iface.launch(inbrowser=True, debug=True)
344
  #iface.launch(auth=("test", "test"), share=True, debug=True)
clip_data.ipynb CHANGED
@@ -2,9 +2,49 @@
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
- "execution_count": 2,
6
  "metadata": {},
7
  "outputs": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  {
9
  "name": "stdout",
10
  "output_type": "stream",
@@ -34,7 +74,7 @@
34
  "name": "stderr",
35
  "output_type": "stream",
36
  "text": [
37
- " \r"
38
  ]
39
  },
40
  {
@@ -107,11 +147,244 @@
107
  " video.write_videofile(output_filename, audio_codec='aac')\n",
108
  "\n",
109
  "# edit_remove_part(\"videos/Ploumen.mp4\", start_s = 5.0, end_s = 10.0)\n",
110
- "edit_change_order(\"videos/Ploumen.mp4\", start_s = 5.0, end_s = 10.0, insert_s = 15.0)\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  "\n",
 
 
 
 
112
  "\n"
113
  ]
114
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  {
116
  "cell_type": "code",
117
  "execution_count": null,
 
2
  "cells": [
3
  {
4
  "cell_type": "code",
5
+ "execution_count": 5,
6
  "metadata": {},
7
  "outputs": [
8
+ {
9
+ "name": "stderr",
10
+ "output_type": "stream",
11
+ "text": [
12
+ "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.ipify.org:443\n",
13
+ "DEBUG:urllib3.connectionpool:https://api.ipify.org:443 \"GET / HTTP/1.1\" 200 15\n",
14
+ "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.gradio.app:443\n",
15
+ "DEBUG:urllib3.connectionpool:https://api.gradio.app:443 \"POST /gradio-initiated-analytics/ HTTP/1.1\" 200 31\n",
16
+ "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.gradio.app:443\n",
17
+ "DEBUG:urllib3.connectionpool:https://api.gradio.app:443 \"POST /gradio-initiated-analytics/ HTTP/1.1\" 200 31\n",
18
+ "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.gradio.app:443\n",
19
+ "DEBUG:urllib3.connectionpool:https://api.gradio.app:443 \"GET /pkg-version HTTP/1.1\" 200 20\n",
20
+ "DEBUG:asyncio:Using selector: KqueueSelector\n",
21
+ "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.ipify.org:443\n"
22
+ ]
23
+ },
24
+ {
25
+ "name": "stdout",
26
+ "output_type": "stream",
27
+ "text": [
28
+ "Using cache from '/Users/ijanssen/videomatch/gradio_cached_examples/15' directory. If method or examples have changed since last caching, delete this folder to clear cache.\n"
29
+ ]
30
+ },
31
+ {
32
+ "name": "stderr",
33
+ "output_type": "stream",
34
+ "text": [
35
+ "DEBUG:urllib3.connectionpool:https://api.ipify.org:443 \"GET / HTTP/1.1\" 200 15\n",
36
+ "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.gradio.app:443\n",
37
+ "DEBUG:urllib3.connectionpool:https://api.gradio.app:443 \"POST /gradio-initiated-analytics/ HTTP/1.1\" 200 31\n",
38
+ "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.gradio.app:443\n",
39
+ "DEBUG:urllib3.connectionpool:https://api.gradio.app:443 \"POST /gradio-initiated-analytics/ HTTP/1.1\" 200 31\n",
40
+ "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.gradio.app:443\n",
41
+ "DEBUG:urllib3.connectionpool:https://api.gradio.app:443 \"GET /pkg-version HTTP/1.1\" 200 20\n",
42
+ "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.ipify.org:443\n",
43
+ "DEBUG:urllib3.connectionpool:https://api.ipify.org:443 \"GET / HTTP/1.1\" 200 15\n",
44
+ "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.gradio.app:443\n",
45
+ "DEBUG:urllib3.connectionpool:https://api.gradio.app:443 \"POST /gradio-initiated-analytics/ HTTP/1.1\" 200 31\n"
46
+ ]
47
+ },
48
  {
49
  "name": "stdout",
50
  "output_type": "stream",
 
74
  "name": "stderr",
75
  "output_type": "stream",
76
  "text": [
77
+ " \r"
78
  ]
79
  },
80
  {
 
147
  " video.write_videofile(output_filename, audio_codec='aac')\n",
148
  "\n",
149
  "# edit_remove_part(\"videos/Ploumen.mp4\", start_s = 5.0, end_s = 10.0)\n",
150
+ "# edit_change_order(\"videos/Ploumen.mp4\", start_s = 5.0, end_s = 10.0, insert_s = 15.0)\n",
151
+ "\n",
152
+ "\n"
153
+ ]
154
+ },
155
+ {
156
+ "cell_type": "code",
157
+ "execution_count": 21,
158
+ "metadata": {},
159
+ "outputs": [
160
+ {
161
+ "name": "stdout",
162
+ "output_type": "stream",
163
+ "text": [
164
+ "[(51, 92), (14, 98), (64, 85), (63, 90), (63, 96)]\n",
165
+ "[112, 0, 53, 96, 123]\n"
166
+ ]
167
+ }
168
+ ],
169
+ "source": [
170
+ "\n",
171
+ "import numpy as np\n",
172
+ "\n",
173
+ "MAX = 130\n",
174
+ "\n",
175
+ "# Get some random start_s and end_s pairs where start_s is always lower than end_s\n",
176
+ "rand_start_s = np.random.randint(low=0, high=MAX, size=5)\n",
177
+ "rand_end_s = np.random.randint(low=0, high=MAX, size=5)\n",
178
+ "rand_pairs = zip(rand_start_s, rand_end_s)\n",
179
+ "rand_pairs = [(x,y) if (x < y) else (y, x) for x, y in rand_pairs]\n",
180
+ "\n",
181
+ "def get_insert_s(start_s, end_s, max=MAX):\n",
182
+ " \"\"\" Get a insert_s that is outside the start_s and end_s \"\"\"\n",
183
+ " random_choice = bool(np.random.randint(low=0, high=2, size=1)[0])\n",
184
+ " if random_choice:\n",
185
+ " return np.random.randint(low=0, high=start_s, size=1)[0]\n",
186
+ " else:\n",
187
+ " return np.random.randint(low=end_s, high=MAX, size=1)[0]\n",
188
  "\n",
189
+ "rand_insert_s = [get_insert_s(x, y) for x, y in rand_pairs]\n",
190
+ "\n",
191
+ "print(rand_pairs)\n",
192
+ "print(rand_insert_s)\n",
193
  "\n"
194
  ]
195
  },
196
+ {
197
+ "cell_type": "code",
198
+ "execution_count": 22,
199
+ "metadata": {},
200
+ "outputs": [
201
+ {
202
+ "name": "stdout",
203
+ "output_type": "stream",
204
+ "text": [
205
+ "Part Start = 51.0, Part Cutout = 41, Part Mid = 20, Part End = 38.03\n",
206
+ "Moviepy - Building video videos/Ploumen_CO_51s_to_92_at_112.mp4.\n",
207
+ "MoviePy - Writing audio in Ploumen_CO_51s_to_92_at_112TEMP_MPY_wvf_snd.mp4\n"
208
+ ]
209
+ },
210
+ {
211
+ "name": "stderr",
212
+ "output_type": "stream",
213
+ "text": [
214
+ " \r"
215
+ ]
216
+ },
217
+ {
218
+ "name": "stdout",
219
+ "output_type": "stream",
220
+ "text": [
221
+ "MoviePy - Done.\n",
222
+ "Moviepy - Writing video videos/Ploumen_CO_51s_to_92_at_112.mp4\n",
223
+ "\n"
224
+ ]
225
+ },
226
+ {
227
+ "name": "stderr",
228
+ "output_type": "stream",
229
+ "text": [
230
+ " \r"
231
+ ]
232
+ },
233
+ {
234
+ "name": "stdout",
235
+ "output_type": "stream",
236
+ "text": [
237
+ "Moviepy - Done !\n",
238
+ "Moviepy - video ready videos/Ploumen_CO_51s_to_92_at_112.mp4\n",
239
+ "Moviepy - Building video videos/Ploumen_CO_14s_to_98_at_0.mp4.\n",
240
+ "MoviePy - Writing audio in Ploumen_CO_14s_to_98_at_0TEMP_MPY_wvf_snd.mp4\n"
241
+ ]
242
+ },
243
+ {
244
+ "name": "stderr",
245
+ "output_type": "stream",
246
+ "text": [
247
+ " \r"
248
+ ]
249
+ },
250
+ {
251
+ "name": "stdout",
252
+ "output_type": "stream",
253
+ "text": [
254
+ "MoviePy - Done.\n",
255
+ "Moviepy - Writing video videos/Ploumen_CO_14s_to_98_at_0.mp4\n",
256
+ "\n"
257
+ ]
258
+ },
259
+ {
260
+ "name": "stderr",
261
+ "output_type": "stream",
262
+ "text": [
263
+ " \r"
264
+ ]
265
+ },
266
+ {
267
+ "name": "stdout",
268
+ "output_type": "stream",
269
+ "text": [
270
+ "Moviepy - Done !\n",
271
+ "Moviepy - video ready videos/Ploumen_CO_14s_to_98_at_0.mp4\n",
272
+ "Moviepy - Building video videos/Ploumen_CO_64s_to_85_at_53.mp4.\n",
273
+ "MoviePy - Writing audio in Ploumen_CO_64s_to_85_at_53TEMP_MPY_wvf_snd.mp4\n"
274
+ ]
275
+ },
276
+ {
277
+ "name": "stderr",
278
+ "output_type": "stream",
279
+ "text": [
280
+ " \r"
281
+ ]
282
+ },
283
+ {
284
+ "name": "stdout",
285
+ "output_type": "stream",
286
+ "text": [
287
+ "MoviePy - Done.\n",
288
+ "Moviepy - Writing video videos/Ploumen_CO_64s_to_85_at_53.mp4\n",
289
+ "\n"
290
+ ]
291
+ },
292
+ {
293
+ "name": "stderr",
294
+ "output_type": "stream",
295
+ "text": [
296
+ " \r"
297
+ ]
298
+ },
299
+ {
300
+ "name": "stdout",
301
+ "output_type": "stream",
302
+ "text": [
303
+ "Moviepy - Done !\n",
304
+ "Moviepy - video ready videos/Ploumen_CO_64s_to_85_at_53.mp4\n",
305
+ "Part Start = 63.0, Part Cutout = 27, Part Mid = 6, Part End = 54.03\n",
306
+ "Moviepy - Building video videos/Ploumen_CO_63s_to_90_at_96.mp4.\n",
307
+ "MoviePy - Writing audio in Ploumen_CO_63s_to_90_at_96TEMP_MPY_wvf_snd.mp4\n"
308
+ ]
309
+ },
310
+ {
311
+ "name": "stderr",
312
+ "output_type": "stream",
313
+ "text": [
314
+ " \r"
315
+ ]
316
+ },
317
+ {
318
+ "name": "stdout",
319
+ "output_type": "stream",
320
+ "text": [
321
+ "MoviePy - Done.\n",
322
+ "Moviepy - Writing video videos/Ploumen_CO_63s_to_90_at_96.mp4\n",
323
+ "\n"
324
+ ]
325
+ },
326
+ {
327
+ "name": "stderr",
328
+ "output_type": "stream",
329
+ "text": [
330
+ " \r"
331
+ ]
332
+ },
333
+ {
334
+ "name": "stdout",
335
+ "output_type": "stream",
336
+ "text": [
337
+ "Moviepy - Done !\n",
338
+ "Moviepy - video ready videos/Ploumen_CO_63s_to_90_at_96.mp4\n",
339
+ "Part Start = 63.0, Part Cutout = 33, Part Mid = 27, Part End = 27.03\n",
340
+ "Moviepy - Building video videos/Ploumen_CO_63s_to_96_at_123.mp4.\n",
341
+ "MoviePy - Writing audio in Ploumen_CO_63s_to_96_at_123TEMP_MPY_wvf_snd.mp4\n"
342
+ ]
343
+ },
344
+ {
345
+ "name": "stderr",
346
+ "output_type": "stream",
347
+ "text": [
348
+ " \r"
349
+ ]
350
+ },
351
+ {
352
+ "name": "stdout",
353
+ "output_type": "stream",
354
+ "text": [
355
+ "MoviePy - Done.\n",
356
+ "Moviepy - Writing video videos/Ploumen_CO_63s_to_96_at_123.mp4\n",
357
+ "\n"
358
+ ]
359
+ },
360
+ {
361
+ "name": "stderr",
362
+ "output_type": "stream",
363
+ "text": [
364
+ " \r"
365
+ ]
366
+ },
367
+ {
368
+ "name": "stdout",
369
+ "output_type": "stream",
370
+ "text": [
371
+ "Moviepy - Done !\n",
372
+ "Moviepy - video ready videos/Ploumen_CO_63s_to_96_at_123.mp4\n"
373
+ ]
374
+ }
375
+ ],
376
+ "source": [
377
+ "for pair, insert_s in zip(rand_pairs, rand_insert_s):\n",
378
+ " edit_change_order(\"videos/Ploumen.mp4\", start_s = pair[0], end_s = pair[1], insert_s = insert_s)"
379
+ ]
380
+ },
381
+ {
382
+ "cell_type": "code",
383
+ "execution_count": null,
384
+ "metadata": {},
385
+ "outputs": [],
386
+ "source": []
387
+ },
388
  {
389
  "cell_type": "code",
390
  "execution_count": null,