Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -8,6 +8,9 @@ import tempfile
|
|
| 8 |
import shutil
|
| 9 |
from tqdm import tqdm
|
| 10 |
|
|
|
|
|
|
|
|
|
|
| 11 |
def get_video_duration(video_path):
|
| 12 |
cap = cv2.VideoCapture(video_path)
|
| 13 |
fps = cap.get(cv2.CAP_PROP_FPS)
|
|
@@ -17,6 +20,12 @@ def get_video_duration(video_path):
|
|
| 17 |
return duration
|
| 18 |
|
| 19 |
def extract_frames(video_path, output_dir, start_time, end_time, max_frames=10000, progress=gr.Progress()):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
cap = cv2.VideoCapture(video_path)
|
| 21 |
fps = cap.get(cv2.CAP_PROP_FPS)
|
| 22 |
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
|
@@ -29,7 +38,7 @@ def extract_frames(video_path, output_dir, start_time, end_time, max_frames=1000
|
|
| 29 |
|
| 30 |
frame_interval = max(1, (end_frame - start_frame) // max_frames)
|
| 31 |
|
| 32 |
-
for i in progress.tqdm(range(start_frame, min(end_frame, total_frames), frame_interval), desc="νλ μ μΆμΆ μ€"):
|
| 33 |
cap.set(cv2.CAP_PROP_POS_FRAMES, i)
|
| 34 |
ret, frame = cap.read()
|
| 35 |
if not ret:
|
|
@@ -41,6 +50,34 @@ def extract_frames(video_path, output_dir, start_time, end_time, max_frames=1000
|
|
| 41 |
cap.release()
|
| 42 |
return frames
|
| 43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
def create_grid(frames, grid_size=(10, 10)):
|
| 45 |
rows, cols = grid_size
|
| 46 |
n_frames = len(frames)
|
|
@@ -75,7 +112,11 @@ def update_slider(video):
|
|
| 75 |
duration = get_video_duration(video.name)
|
| 76 |
return gr.Slider(minimum=0, maximum=duration, value=0, step=0.1, label="CUT μμ μκ° (μ΄)"), gr.Slider(minimum=0, maximum=duration, value=duration, step=0.1, label="CUT μ’
λ£ μκ° (μ΄)")
|
| 77 |
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
if video is None:
|
| 80 |
return None, None, "λΉλμ€ νμΌμ μ
λ‘λν΄μ£ΌμΈμ."
|
| 81 |
|
|
@@ -97,37 +138,12 @@ def process_video(video, start_time, end_time, progress=gr.Progress()):
|
|
| 97 |
final_zip_path = "frames.zip"
|
| 98 |
shutil.copy(zip_path, final_zip_path)
|
| 99 |
|
| 100 |
-
|
|
|
|
| 101 |
except Exception as e:
|
| 102 |
return None, None, f"μ€λ₯κ° λ°μνμ΅λλ€: {str(e)}"
|
| 103 |
|
| 104 |
-
|
| 105 |
-
footer {
|
| 106 |
-
visibility: hidden;
|
| 107 |
-
}
|
| 108 |
-
#video-time {
|
| 109 |
-
font-size: 1.2em;
|
| 110 |
-
font-weight: bold;
|
| 111 |
-
margin-top: 10px;
|
| 112 |
-
}
|
| 113 |
-
"""
|
| 114 |
-
|
| 115 |
-
js = """
|
| 116 |
-
function updateVideoTime(videoElement) {
|
| 117 |
-
const timeDisplay = document.getElementById('video-time');
|
| 118 |
-
videoElement.addEventListener('timeupdate', () => {
|
| 119 |
-
const currentTime = videoElement.currentTime.toFixed(2);
|
| 120 |
-
timeDisplay.textContent = `νμ¬ μ¬μ μκ°: ${currentTime}μ΄`;
|
| 121 |
-
});
|
| 122 |
-
}
|
| 123 |
-
document.addEventListener('DOMContentLoaded', () => {
|
| 124 |
-
const videoElement = document.querySelector('video');
|
| 125 |
-
if (videoElement) {
|
| 126 |
-
updateVideoTime(videoElement);
|
| 127 |
-
}
|
| 128 |
-
});
|
| 129 |
-
"""
|
| 130 |
-
|
| 131 |
with gr.Blocks(theme="Nymbo/Nymbo_Theme", css=css, js=js) as iface:
|
| 132 |
gr.Markdown("# λΉλμ€ to μ΄λ―Έμ§(νλ μ)")
|
| 133 |
gr.Markdown("μνλ ꡬκ°μ νλ μμ μΆμΆνμ¬ κ·Έλ¦¬λλ‘ νμνκ³ , κ°λ³ μ΄λ―Έμ§λ₯Ό ZIP οΏ½οΏ½οΏ½μΌλ‘ λ€μ΄λ‘λν μ μμ΅λλ€.")
|
|
@@ -143,6 +159,9 @@ with gr.Blocks(theme="Nymbo/Nymbo_Theme", css=css, js=js) as iface:
|
|
| 143 |
start_slider = gr.Slider(minimum=0, maximum=10000, value=0, step=1, label="CUT μμ μκ° (μ΄)")
|
| 144 |
end_slider = gr.Slider(minimum=0, maximum=10000, value=10000, step=1, label="CUT μ’
λ£ μκ° (μ΄)")
|
| 145 |
|
|
|
|
|
|
|
|
|
|
| 146 |
with gr.Row():
|
| 147 |
process_button = gr.Button("νλ μ μΆμΆ")
|
| 148 |
|
|
@@ -153,7 +172,7 @@ with gr.Blocks(theme="Nymbo/Nymbo_Theme", css=css, js=js) as iface:
|
|
| 153 |
message_output = gr.Textbox(label="λ©μμ§")
|
| 154 |
|
| 155 |
video_input.change(update_slider, inputs=[video_input], outputs=[start_slider, end_slider])
|
| 156 |
-
process_button.click(process_video, inputs=[video_input, start_slider, end_slider], outputs=[image_output, file_output, message_output])
|
| 157 |
|
| 158 |
if __name__ == "__main__":
|
| 159 |
iface.launch(share=True)
|
|
|
|
| 8 |
import shutil
|
| 9 |
from tqdm import tqdm
|
| 10 |
|
| 11 |
+
# GPU μ¬μ© κ°λ₯ μ¬λΆ νμΈ
|
| 12 |
+
USE_GPU = cv2.cuda.getCudaEnabledDeviceCount() > 0
|
| 13 |
+
|
| 14 |
def get_video_duration(video_path):
|
| 15 |
cap = cv2.VideoCapture(video_path)
|
| 16 |
fps = cap.get(cv2.CAP_PROP_FPS)
|
|
|
|
| 20 |
return duration
|
| 21 |
|
| 22 |
def extract_frames(video_path, output_dir, start_time, end_time, max_frames=10000, progress=gr.Progress()):
|
| 23 |
+
if USE_GPU:
|
| 24 |
+
return extract_frames_gpu(video_path, output_dir, start_time, end_time, max_frames, progress)
|
| 25 |
+
else:
|
| 26 |
+
return extract_frames_cpu(video_path, output_dir, start_time, end_time, max_frames, progress)
|
| 27 |
+
|
| 28 |
+
def extract_frames_cpu(video_path, output_dir, start_time, end_time, max_frames, progress):
|
| 29 |
cap = cv2.VideoCapture(video_path)
|
| 30 |
fps = cap.get(cv2.CAP_PROP_FPS)
|
| 31 |
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
|
|
|
| 38 |
|
| 39 |
frame_interval = max(1, (end_frame - start_frame) // max_frames)
|
| 40 |
|
| 41 |
+
for i in progress.tqdm(range(start_frame, min(end_frame, total_frames), frame_interval), desc="νλ μ μΆμΆ μ€ (CPU)"):
|
| 42 |
cap.set(cv2.CAP_PROP_POS_FRAMES, i)
|
| 43 |
ret, frame = cap.read()
|
| 44 |
if not ret:
|
|
|
|
| 50 |
cap.release()
|
| 51 |
return frames
|
| 52 |
|
| 53 |
+
def extract_frames_gpu(video_path, output_dir, start_time, end_time, max_frames, progress):
|
| 54 |
+
stream = cv2.cuda_Stream()
|
| 55 |
+
cap = cv2.cudacodec.createVideoReader(video_path)
|
| 56 |
+
|
| 57 |
+
_, first_frame = cap.nextFrame()
|
| 58 |
+
fps = cap.get(cv2.cudacodec.ColorFormat.FPS)
|
| 59 |
+
total_frames = int(cap.get(cv2.cudacodec.ColorFormat.NUMBER_OF_FRAMES))
|
| 60 |
+
|
| 61 |
+
start_frame = int(start_time * fps)
|
| 62 |
+
end_frame = int(end_time * fps) if end_time else total_frames
|
| 63 |
+
|
| 64 |
+
frames = []
|
| 65 |
+
cap.set(cv2.cudacodec.ColorFormat.POS_FRAMES, start_frame)
|
| 66 |
+
|
| 67 |
+
frame_interval = max(1, (end_frame - start_frame) // max_frames)
|
| 68 |
+
|
| 69 |
+
for i in progress.tqdm(range(start_frame, min(end_frame, total_frames), frame_interval), desc="νλ μ μΆμΆ μ€ (GPU)"):
|
| 70 |
+
cap.set(cv2.cudacodec.ColorFormat.POS_FRAMES, i)
|
| 71 |
+
ret, gpu_frame = cap.nextFrame()
|
| 72 |
+
if not ret:
|
| 73 |
+
break
|
| 74 |
+
cpu_frame = gpu_frame.download()
|
| 75 |
+
frames.append(cpu_frame)
|
| 76 |
+
if len(frames) >= max_frames:
|
| 77 |
+
break
|
| 78 |
+
|
| 79 |
+
return frames
|
| 80 |
+
|
| 81 |
def create_grid(frames, grid_size=(10, 10)):
|
| 82 |
rows, cols = grid_size
|
| 83 |
n_frames = len(frames)
|
|
|
|
| 112 |
duration = get_video_duration(video.name)
|
| 113 |
return gr.Slider(minimum=0, maximum=duration, value=0, step=0.1, label="CUT μμ μκ° (μ΄)"), gr.Slider(minimum=0, maximum=duration, value=duration, step=0.1, label="CUT μ’
λ£ μκ° (μ΄)")
|
| 114 |
|
| 115 |
+
|
| 116 |
+
def process_video(video, start_time, end_time, use_gpu, progress=gr.Progress()):
|
| 117 |
+
global USE_GPU
|
| 118 |
+
USE_GPU = use_gpu and cv2.cuda.getCudaEnabledDeviceCount() > 0
|
| 119 |
+
|
| 120 |
if video is None:
|
| 121 |
return None, None, "λΉλμ€ νμΌμ μ
λ‘λν΄μ£ΌμΈμ."
|
| 122 |
|
|
|
|
| 138 |
final_zip_path = "frames.zip"
|
| 139 |
shutil.copy(zip_path, final_zip_path)
|
| 140 |
|
| 141 |
+
gpu_status = "GPU" if USE_GPU else "CPU"
|
| 142 |
+
return Image.fromarray(cv2.cvtColor(grid, cv2.COLOR_BGR2RGB)), final_zip_path, f"νλ μ μΆμΆμ΄ μλ£λμμ΅λλ€ ({gpu_status} μ¬μ©). μ΄ {len(frames)}κ°μ νλ μμ΄ μΆμΆλμμ΅λλ€."
|
| 143 |
except Exception as e:
|
| 144 |
return None, None, f"μ€λ₯κ° λ°μνμ΅λλ€: {str(e)}"
|
| 145 |
|
| 146 |
+
# Gradio μΈν°νμ΄μ€ μ€μ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
with gr.Blocks(theme="Nymbo/Nymbo_Theme", css=css, js=js) as iface:
|
| 148 |
gr.Markdown("# λΉλμ€ to μ΄λ―Έμ§(νλ μ)")
|
| 149 |
gr.Markdown("μνλ ꡬκ°μ νλ μμ μΆμΆνμ¬ κ·Έλ¦¬λλ‘ νμνκ³ , κ°λ³ μ΄λ―Έμ§λ₯Ό ZIP οΏ½οΏ½οΏ½μΌλ‘ λ€μ΄λ‘λν μ μμ΅λλ€.")
|
|
|
|
| 159 |
start_slider = gr.Slider(minimum=0, maximum=10000, value=0, step=1, label="CUT μμ μκ° (μ΄)")
|
| 160 |
end_slider = gr.Slider(minimum=0, maximum=10000, value=10000, step=1, label="CUT μ’
λ£ μκ° (μ΄)")
|
| 161 |
|
| 162 |
+
with gr.Row():
|
| 163 |
+
use_gpu_checkbox = gr.Checkbox(label="GPU μ¬μ© (κ°λ₯ν κ²½μ°)", value=USE_GPU)
|
| 164 |
+
|
| 165 |
with gr.Row():
|
| 166 |
process_button = gr.Button("νλ μ μΆμΆ")
|
| 167 |
|
|
|
|
| 172 |
message_output = gr.Textbox(label="λ©μμ§")
|
| 173 |
|
| 174 |
video_input.change(update_slider, inputs=[video_input], outputs=[start_slider, end_slider])
|
| 175 |
+
process_button.click(process_video, inputs=[video_input, start_slider, end_slider, use_gpu_checkbox], outputs=[image_output, file_output, message_output])
|
| 176 |
|
| 177 |
if __name__ == "__main__":
|
| 178 |
iface.launch(share=True)
|