Spaces:
Running
on
Zero
Running
on
Zero
Upload 2 files
Browse files- app.py +279 -0
- requirements.txt +8 -0
app.py
ADDED
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import onnxruntime as ort
|
2 |
+
from huggingface_hub import hf_hub_download
|
3 |
+
import requests
|
4 |
+
import os
|
5 |
+
import gradio as gr
|
6 |
+
import spaces
|
7 |
+
from typing import Any, List, Callable
|
8 |
+
import cv2
|
9 |
+
import insightface
|
10 |
+
import time
|
11 |
+
import tempfile
|
12 |
+
import subprocess
|
13 |
+
import gfpgan
|
14 |
+
|
15 |
+
print("Installing cuDNN 9")
|
16 |
+
|
17 |
+
import subprocess
|
18 |
+
import sys
|
19 |
+
|
20 |
+
def get_pip_version(package_name):
|
21 |
+
try:
|
22 |
+
result = subprocess.run(
|
23 |
+
[sys.executable, '-m', 'pip', 'show', package_name],
|
24 |
+
capture_output=True,
|
25 |
+
text=True,
|
26 |
+
check=True
|
27 |
+
)
|
28 |
+
output = result.stdout
|
29 |
+
version_line = next(line for line in output.split('\n') if line.startswith('Version:'))
|
30 |
+
return version_line.split(': ')[1]
|
31 |
+
except subprocess.CalledProcessError as e:
|
32 |
+
print(f"Error executing command: {e}")
|
33 |
+
return None
|
34 |
+
|
35 |
+
package_name = 'nvidia-cudnn-cu12'
|
36 |
+
version = get_pip_version(package_name)
|
37 |
+
print(f"The installed version of {package_name} is: {version}")
|
38 |
+
|
39 |
+
command = "find / -path /proc -prune -o -path /sys -prune -o -name 'libcudnn*' -print"
|
40 |
+
process = subprocess.run(command, shell=True, text=True, capture_output=True)
|
41 |
+
|
42 |
+
if process.returncode == 0:
|
43 |
+
print("Search results:\n", process.stdout)
|
44 |
+
else:
|
45 |
+
print("An error occurred while executing the command:", process.stderr)
|
46 |
+
|
47 |
+
source_path = '/usr/local/lib/python3.10/site-packages/nvidia/cublas/lib/libcublasLt.so.12'
|
48 |
+
destination_path = '/usr/local/lib/python3.10/site-packages/nvidia/cudnn/lib/'
|
49 |
+
|
50 |
+
commands = [
|
51 |
+
['mv', source_path, destination_path],
|
52 |
+
['mv', "/usr/local/lib/python3.10/site-packages/nvidia/cublas/lib/libcublas.so.12", destination_path],
|
53 |
+
['mv', "/usr/local/lib/python3.10/site-packages/nvidia/cufft/lib/libcufft.so.11", destination_path],
|
54 |
+
['mv', "/usr/local/lib/python3.10/site-packages/nvidia/cufft/lib/libcufftw.so.11", destination_path],
|
55 |
+
['mv', "/usr/local/lib/python3.10/site-packages/nvidia/cuda_runtime/lib/libcudart.so.12", destination_path],
|
56 |
+
['mv', "/usr/local/lib/python3.10/site-packages/nvidia/cuda_cupti/lib/libcupti.so.12", destination_path],
|
57 |
+
['cp', "/usr/local/lib/python3.10/site-packages/nvidia/curand/lib/libcurand.so.10", destination_path],
|
58 |
+
['cp', "/usr/local/lib/python3.10/site-packages/nvidia/cusolver/lib/libcusolver.so.11", destination_path],
|
59 |
+
['cp', "/usr/local/lib/python3.10/site-packages/nvidia/cusolver/lib/libcusolverMg.so.11", destination_path],
|
60 |
+
['cp', "/usr/local/lib/python3.10/site-packages/nvidia/cusparse/lib/libcusparse.so.12", destination_path],
|
61 |
+
]
|
62 |
+
|
63 |
+
for command in commands:
|
64 |
+
subprocess.run(command, check=True)
|
65 |
+
|
66 |
+
command = "find / -path /proc -prune -o -path /sys -prune -o -name 'libcu*' -print"
|
67 |
+
process = subprocess.run(command, shell=True, text=True, capture_output=True)
|
68 |
+
|
69 |
+
if process.returncode == 0:
|
70 |
+
print("Search results:\n", process.stdout)
|
71 |
+
else:
|
72 |
+
print("An error occurred while executing the command:", process.stderr)
|
73 |
+
|
74 |
+
print("Done")
|
75 |
+
print("---------------------")
|
76 |
+
print(ort.get_available_providers())
|
77 |
+
|
78 |
+
def conditional_download(download_directory_path, urls):
|
79 |
+
if not os.path.exists(download_directory_path):
|
80 |
+
os.makedirs(download_directory_path)
|
81 |
+
for url in urls:
|
82 |
+
filename = url.split('/')[-1]
|
83 |
+
file_path = os.path.join(download_directory_path, filename)
|
84 |
+
if not os.path.exists(file_path):
|
85 |
+
print(f"Downloading {filename}...")
|
86 |
+
response = requests.get(url, stream=True)
|
87 |
+
if response.status_code == 200:
|
88 |
+
with open(file_path, 'wb') as file:
|
89 |
+
for chunk in response.iter_content(chunk_size=8192):
|
90 |
+
file.write(chunk)
|
91 |
+
print(f"{filename} downloaded successfully.")
|
92 |
+
else:
|
93 |
+
print(f"Failed to download {filename}. Status code: {response.status_code}")
|
94 |
+
else:
|
95 |
+
print(f"{filename} already exists. Skipping download.")
|
96 |
+
|
97 |
+
model_path = hf_hub_download(repo_id="countfloyd/deepfake", filename="inswapper_128.onnx")
|
98 |
+
conditional_download("./", ['https://github.com/TencentARC/GFPGAN/releases/download/v1.3.4/GFPGANv1.4.pth'])
|
99 |
+
|
100 |
+
FACE_SWAPPER = None
|
101 |
+
FACE_ANALYSER = None
|
102 |
+
FACE_ENHANCER = None
|
103 |
+
|
104 |
+
@spaces.GPU(duration=300, enable_queue=True)
|
105 |
+
def process_video(source_path: str, target_path: str, enhance=False, progress=gr.Progress(), output_path='result.mp4') -> None:
|
106 |
+
def get_face_analyser():
|
107 |
+
global FACE_ANALYSER
|
108 |
+
if FACE_ANALYSER is None:
|
109 |
+
FACE_ANALYSER = insightface.app.FaceAnalysis(name='buffalo_l', providers=["CUDAExecutionProvider"])
|
110 |
+
FACE_ANALYSER.prepare(ctx_id=0, det_size=(640, 640))
|
111 |
+
return FACE_ANALYSER
|
112 |
+
|
113 |
+
def get_face_enhancer() -> Any:
|
114 |
+
global FACE_ENHANCER
|
115 |
+
if FACE_ENHANCER is None:
|
116 |
+
FACE_ENHANCER = gfpgan.GFPGANer(model_path="./GFPGANv1.4.pth", upscale=2) # type: ignore[attr-defined]
|
117 |
+
return FACE_ENHANCER
|
118 |
+
|
119 |
+
def get_one_face(frame):
|
120 |
+
face = get_face_analyser().get(frame)
|
121 |
+
try:
|
122 |
+
return min(face, key=lambda x: x.bbox[0])
|
123 |
+
except ValueError:
|
124 |
+
return None
|
125 |
+
|
126 |
+
def get_face_swapper():
|
127 |
+
global FACE_SWAPPER
|
128 |
+
if FACE_SWAPPER is None:
|
129 |
+
FACE_SWAPPER = insightface.model_zoo.get_model(model_path, providers=["CUDAExecutionProvider"])
|
130 |
+
return FACE_SWAPPER
|
131 |
+
|
132 |
+
def swap_face(source_face, target_face, temp_frame):
|
133 |
+
return get_face_swapper().get(temp_frame, target_face, source_face, paste_back=True)
|
134 |
+
|
135 |
+
def process_frame(source_face, temp_frame, enhance):
|
136 |
+
target_face = get_one_face(temp_frame)
|
137 |
+
if target_face:
|
138 |
+
temp_frame = swap_face(source_face, target_face, temp_frame)
|
139 |
+
if enhance:
|
140 |
+
temp_frame = enhance_face(temp_frame)
|
141 |
+
return temp_frame
|
142 |
+
|
143 |
+
def process_image(source_path: str, target_path: str, output_path: str) -> None:
|
144 |
+
source_face = get_one_face(cv2.imread(source_path))
|
145 |
+
target_frame = cv2.imread(target_path)
|
146 |
+
result = process_frame(source_face, target_frame)
|
147 |
+
cv2.imwrite(output_path, result)
|
148 |
+
|
149 |
+
def create_temp_directory():
|
150 |
+
temp_dir = tempfile.mkdtemp()
|
151 |
+
return temp_dir
|
152 |
+
|
153 |
+
def enhance_face(temp_frame):
|
154 |
+
_, _, temp_frame = get_face_enhancer().enhance(
|
155 |
+
temp_frame,
|
156 |
+
paste_back=True
|
157 |
+
)
|
158 |
+
return temp_frame
|
159 |
+
|
160 |
+
def remove_temp_directory(temp_dir):
|
161 |
+
try:
|
162 |
+
for filename in os.listdir(temp_dir):
|
163 |
+
file_path = os.path.join(temp_dir, filename)
|
164 |
+
if os.path.isfile(file_path):
|
165 |
+
os.unlink(file_path)
|
166 |
+
elif os.path.isdir(file_path):
|
167 |
+
os.rmdir(file_path)
|
168 |
+
os.rmdir(temp_dir)
|
169 |
+
except Exception as e:
|
170 |
+
print(f"Error removing temporary directory: {e}")
|
171 |
+
|
172 |
+
def extract_frames(video_path: str):
|
173 |
+
video_capture = cv2.VideoCapture(video_path)
|
174 |
+
if not video_capture.isOpened():
|
175 |
+
print("Error opening video.")
|
176 |
+
return []
|
177 |
+
frames = []
|
178 |
+
while True:
|
179 |
+
ret, frame = video_capture.read()
|
180 |
+
if not ret:
|
181 |
+
break
|
182 |
+
frames.append(frame)
|
183 |
+
video_capture.release()
|
184 |
+
return frames
|
185 |
+
|
186 |
+
def get_video_fps(video_path: str) -> float:
|
187 |
+
video_capture = cv2.VideoCapture(video_path)
|
188 |
+
if not video_capture.isOpened():
|
189 |
+
raise ValueError("Error opening video.")
|
190 |
+
fps = video_capture.get(cv2.CAP_PROP_FPS)
|
191 |
+
video_capture.release()
|
192 |
+
return fps
|
193 |
+
|
194 |
+
def create_video_from_frames(temp_dir: str, output_video_path: str, fps: float) -> None:
|
195 |
+
temp_frames_pattern = os.path.join(temp_dir, "frame_%04d.jpg")
|
196 |
+
ffmpeg_command = [
|
197 |
+
'ffmpeg',
|
198 |
+
'-y',
|
199 |
+
'-framerate', str(fps),
|
200 |
+
'-i', temp_frames_pattern,
|
201 |
+
'-c:v', 'libx264',
|
202 |
+
'-pix_fmt', 'yuv420p',
|
203 |
+
'-preset', 'ultrafast',
|
204 |
+
output_video_path
|
205 |
+
]
|
206 |
+
subprocess.run(ffmpeg_command, check=True)
|
207 |
+
|
208 |
+
def extract_audio(video_path: str, audio_path: str) -> None:
|
209 |
+
ffmpeg_command = [
|
210 |
+
'ffmpeg',
|
211 |
+
'-y',
|
212 |
+
'-i', video_path,
|
213 |
+
'-q:a', '0',
|
214 |
+
'-map', 'a',
|
215 |
+
'-preset', 'ultrafast',
|
216 |
+
audio_path
|
217 |
+
]
|
218 |
+
subprocess.run(ffmpeg_command, check=True)
|
219 |
+
|
220 |
+
def add_audio_to_video(video_path: str, audio_path: str, output_video_path: str) -> None:
|
221 |
+
ffmpeg_command = [
|
222 |
+
'ffmpeg',
|
223 |
+
'-y',
|
224 |
+
'-i', video_path,
|
225 |
+
'-i', audio_path,
|
226 |
+
'-c:v', 'copy',
|
227 |
+
'-c:a', 'aac',
|
228 |
+
'-strict', 'experimental',
|
229 |
+
'-preset', 'ultrafast',
|
230 |
+
output_video_path
|
231 |
+
]
|
232 |
+
subprocess.run(ffmpeg_command, check=True)
|
233 |
+
|
234 |
+
def delete_file(file_path: str) -> None:
|
235 |
+
try:
|
236 |
+
os.remove(file_path)
|
237 |
+
except Exception as e:
|
238 |
+
print(f"Error removing file: {e}")
|
239 |
+
|
240 |
+
def reduce_video(video_path: str, output_video_path: str) -> None:
|
241 |
+
ffmpeg_command = [
|
242 |
+
'ffmpeg',
|
243 |
+
'-y',
|
244 |
+
'-i', video_path,
|
245 |
+
'-vf', "scale='if(gte(iw,ih),720,-1)':'if(gte(iw,ih),-1,720)',pad=ceil(iw/2)*2:ceil(ih/2)*2",
|
246 |
+
'-preset', 'ultrafast',
|
247 |
+
'-r', '24',
|
248 |
+
output_video_path
|
249 |
+
]
|
250 |
+
subprocess.run(ffmpeg_command, check=True)
|
251 |
+
|
252 |
+
temp_dir = create_temp_directory()
|
253 |
+
video_input = temp_dir + "/input.mp4"
|
254 |
+
reduce_video(target_path, video_input)
|
255 |
+
|
256 |
+
source_face = get_one_face(cv2.imread(source_path))
|
257 |
+
frames = extract_frames(video_input)
|
258 |
+
|
259 |
+
for index, frame in progress.tqdm(enumerate(frames), total=len(frames)):
|
260 |
+
processed_frame = process_frame(source_face, frame, enhance)
|
261 |
+
frame_filename = os.path.join(temp_dir, f"frame_{index:04d}.jpg")
|
262 |
+
cv2.imwrite(frame_filename, processed_frame)
|
263 |
+
|
264 |
+
video_path = temp_dir + "/out.mp4"
|
265 |
+
create_video_from_frames(temp_dir, video_path, get_video_fps(video_input))
|
266 |
+
audio_path = temp_dir + "/audio.wav"
|
267 |
+
extract_audio(video_input, audio_path)
|
268 |
+
add_audio_to_video(video_path, audio_path, output_path)
|
269 |
+
remove_temp_directory(temp_dir)
|
270 |
+
return output_path
|
271 |
+
|
272 |
+
app = gr.Interface(
|
273 |
+
fn=process_video,
|
274 |
+
inputs=[gr.Image(type='filepath'), gr.Video(), gr.Checkbox(label="Use Face GAN (increase render time)", value=False)],
|
275 |
+
outputs=[gr.Video()],
|
276 |
+
description="Videos get downsampled to 720p and 24fps. To modify the code or purchase a modification, send an email to [email protected] to donate to the owner of the space: https://donate.stripe.com/3csg0D0tadXU4mYcMM"
|
277 |
+
)
|
278 |
+
|
279 |
+
app.launch()
|
requirements.txt
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
--extra-index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple
|
2 |
+
onnxruntime-gpu
|
3 |
+
insightface
|
4 |
+
gfpgan
|
5 |
+
nvidia-cudnn-cu12==9.3.0.75
|
6 |
+
torchvision
|
7 |
+
opencv-python
|
8 |
+
numpy<2
|