amongusrickroll68 fffiloni commited on
Commit
1e7e0e5
0 Parent(s):

Duplicate from fffiloni/scene-edit-detection

Browse files

Co-authored-by: Sylvain Filoni <fffiloni@users.noreply.huggingface.co>

Files changed (4) hide show
  1. .gitattributes +31 -0
  2. README.md +13 -0
  3. app.py +154 -0
  4. requirements.txt +4 -0
.gitattributes ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ftz filter=lfs diff=lfs merge=lfs -text
6
+ *.gz filter=lfs diff=lfs merge=lfs -text
7
+ *.h5 filter=lfs diff=lfs merge=lfs -text
8
+ *.joblib filter=lfs diff=lfs merge=lfs -text
9
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
10
+ *.model filter=lfs diff=lfs merge=lfs -text
11
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
12
+ *.npy filter=lfs diff=lfs merge=lfs -text
13
+ *.npz filter=lfs diff=lfs merge=lfs -text
14
+ *.onnx filter=lfs diff=lfs merge=lfs -text
15
+ *.ot filter=lfs diff=lfs merge=lfs -text
16
+ *.parquet filter=lfs diff=lfs merge=lfs -text
17
+ *.pickle filter=lfs diff=lfs merge=lfs -text
18
+ *.pkl filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pt filter=lfs diff=lfs merge=lfs -text
21
+ *.pth filter=lfs diff=lfs merge=lfs -text
22
+ *.rar filter=lfs diff=lfs merge=lfs -text
23
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
24
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
25
+ *.tflite filter=lfs diff=lfs merge=lfs -text
26
+ *.tgz filter=lfs diff=lfs merge=lfs -text
27
+ *.wasm filter=lfs diff=lfs merge=lfs -text
28
+ *.xz filter=lfs diff=lfs merge=lfs -text
29
+ *.zip filter=lfs diff=lfs merge=lfs -text
30
+ *.zst filter=lfs diff=lfs merge=lfs -text
31
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Scene Edit Detection
3
+ emoji: ✂️ 🎞
4
+ colorFrom: pink
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 3.4
8
+ app_file: app.py
9
+ pinned: false
10
+ duplicated_from: fffiloni/scene-edit-detection
11
+ ---
12
+
13
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dependencies, see also requirement.txt ;)
2
+ import gradio as gr
3
+ import cv2
4
+ import numpy as np
5
+ import os
6
+
7
+ from scenedetect import open_video, SceneManager
8
+ from scenedetect.detectors import ContentDetector
9
+
10
+ from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
11
+
12
+ # —————————————————————————————————————————————————
13
+
14
+ title = "Scene Edit Detection"
15
+ description = "<p style='text-align: center'>Gradio demo of PySceneDetect. <br />Automatically find every shots in a video sequence</p><p style='text-align: center'> 1. gives you timecode in/out for each shot. 2. saves each shot as a splitted mp4 video chunk for you to download. 3. diplays a thumbnail for each shot as a gallery output.<br /> <img id='visitor-badge' alt='visitor badge' src='https://visitor-badge.glitch.me/badge?page_id=gradio-blocks.scene-edit-detection' style='display: inline-block'/></b></p>"
16
+ article = "<p style='text-align: center'><a href='http://scenedetect.com/en/latest/' target='_blank'>PySceneDetect website</a> | <a href='https://github.com/Breakthrough/PySceneDetect' target='_blank'>Github Repo</a></p>"
17
+
18
+ # —————————————————————————————————————————————————
19
+
20
+ # SET INPUTS
21
+ video_input = gr.Video(source="upload", format="mp4", label="Video Sequence", mirror_webcam=False)
22
+ threshold = gr.Slider(label="Threshold pixel comparison: if exceeded, triggers a scene cut. Default: 27.0", minimum=15.0, maximum=40.0, value=27.0)
23
+
24
+ # —————————————————————————————————————————————————
25
+
26
+ def convert_to_tuple(list):
27
+ return tuple(list);
28
+
29
+
30
+ def find_scenes(video_path, threshold):
31
+ # file name without extension
32
+ filename = os.path.splitext(os.path.basename(video_path))[0]
33
+ # Open our video, create a scene manager, and add a detector.
34
+ video = open_video(video_path)
35
+ scene_manager = SceneManager()
36
+ scene_manager.add_detector(
37
+ ContentDetector(threshold=threshold))
38
+
39
+ # Start detection
40
+ scene_manager.detect_scenes(video, show_progress=True)
41
+ scene_list = scene_manager.get_scene_list()
42
+
43
+ # Push the list of scenes into data_outputs
44
+ data_outputs.append(scene_list)
45
+ gradio_components_outputs.append("json")
46
+ #print(scene_list)
47
+
48
+ timecodes = []
49
+ timecodes.append({"title": filename + ".mp4", "fps": scene_list[0][0].get_framerate()})
50
+
51
+ shots = []
52
+ stills = []
53
+
54
+ # For each shot found, set entry and exit points as seconds from frame number
55
+ # Then split video into chunks and store them into shots List
56
+ # Then extract first frame of each shot as thumbnail for the gallery
57
+ for i, shot in enumerate(scene_list):
58
+
59
+ # STEP 1
60
+ # Get timecode in seconds
61
+ framerate = shot[0].get_framerate()
62
+ shot_in = shot[0].get_frames() / framerate
63
+ shot_out = shot[1].get_frames() / framerate
64
+
65
+ tc_in = shot[0].get_timecode()
66
+ tc_out = shot[1].get_timecode()
67
+
68
+ frame_in = shot[0].get_frames()
69
+ frame_out = shot[1].get_frames()
70
+
71
+ timecode = {"tc_in": tc_in, "tc_out": tc_out, "frame_in": frame_in, "frame_out": frame_out}
72
+ timecodes.append(timecode)
73
+
74
+ # Set name template for each shot
75
+ target_name = "shot_" + str(i+1) + "_" + str(filename) + ".mp4"
76
+
77
+ # Split chunk
78
+ ffmpeg_extract_subclip(video_path, shot_in, shot_out, targetname=target_name)
79
+
80
+ # Push chunk into shots List
81
+ shots.append(target_name)
82
+
83
+ # Push each chunk into data_outputs
84
+ data_outputs.append(target_name)
85
+ gradio_components_outputs.append("video")
86
+
87
+ # —————————————————————————————————————————————————
88
+
89
+ # STEP 2
90
+ # extract first frame of each shot with cv2
91
+ vid = cv2.VideoCapture(video_path)
92
+ fps = vid.get(cv2.CAP_PROP_FPS)
93
+ print('frames per second =',fps)
94
+
95
+ frame_id = shot[0].get_frames() # value from scene_list from step 1
96
+
97
+ vid.set(cv2.CAP_PROP_POS_FRAMES, frame_id)
98
+ ret, frame = vid.read()
99
+
100
+ # Save frame as PNG file
101
+ img = str(frame_id) + '_screenshot.png'
102
+ cv2.imwrite(img,frame)
103
+
104
+ # Push image into stills List
105
+ stills.append((img, 'shot ' + str(i+1)))
106
+
107
+ # Push the list of video shots into data_outputs for Gradio file component
108
+ data_outputs.append(shots)
109
+ gradio_components_outputs.append("file")
110
+
111
+ # Push the list of still images into data_outputs
112
+ data_outputs.append(stills)
113
+ gradio_components_outputs.append("gallery")
114
+
115
+ # This would have been used as gradio outputs,
116
+ # if we could set number of outputs after the interface launch
117
+ # That's not (yet ?) possible
118
+ results = convert_to_tuple(data_outputs)
119
+ print(results)
120
+
121
+ # return List of shots as JSON, List of video chunks, List of still images
122
+ # *
123
+ # Would be nice to be able to return my results tuple as outputs,
124
+ # while number of chunks found is not fixed:
125
+ # return results
126
+ return timecodes, shots, stills
127
+
128
+ # —————————————————————————————————————————————————
129
+
130
+ # SET DATA AND COMPONENTS OUTPUTS
131
+
132
+ # This would be filled like this:
133
+ # data_outputs = [ [List from detection], "video_chunk_n0.mp4", "video_chunk_n1.mp4", ... , "video_chunk_n.mp4", [List of video filepath to download], [List of still images from each shot found] ]
134
+ data_outputs = []
135
+
136
+ # This would be filled like this:
137
+ # gradio_components_outputs = [ "json", "video", "video", ... , "video", "file", "gallery" ]
138
+ gradio_components_outputs = []
139
+
140
+
141
+ #SET OUTPUTS
142
+
143
+ # This would be nice if number of outputs could be set after Interface Launch:
144
+ # because we do not know how many shots will be detected
145
+ # gradio_components_outputs = [ "json", "video", "video", ... , "video", "file", "gallery" ]
146
+ # outputs = gradio_components_outputs
147
+
148
+ # ANOTHER SOLUTION WOULD BE USING A (FUTURE ?) "VIDEO GALLERY" GRADIO COMPONENT FROM LIST :)
149
+
150
+ outputs = [gr.JSON(label="Shots detected"), gr.File(label="Downloadable Shots"), gr.Gallery(label="Still Images from each shot").style(grid=3)]
151
+
152
+ # —————————————————————————————————————————————————
153
+ print('Hello Sylvain')
154
+ gr.Interface(fn=find_scenes, inputs=[video_input, threshold], outputs=outputs, title=title, description=description, article=article).launch()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ opencv-python>=4.4.0
2
+ scenedetect<0.7
3
+ moviepy
4
+ imageio-ffmpeg