2023-05-30 00:40:02 +02:00
#!/usr/bin/env python3
2023-05-30 13:47:44 +02:00
import platform
2023-05-31 21:03:44 +02:00
import signal
2023-05-29 19:04:00 +05:30
import sys
2023-05-29 23:35:29 +05:30
import shutil
2023-05-28 20:19:40 +05:30
import glob
import argparse
import multiprocessing as mp
import os
2023-05-31 16:30:24 +02:00
import torch
2023-05-28 20:19:40 +05:30
from pathlib import Path
import tkinter as tk
from tkinter import filedialog
2023-05-31 19:06:19 +02:00
from opennsfw2 import predict_video_frames , predict_image
2023-05-29 13:39:44 +05:30
from tkinter . filedialog import asksaveasfilename
2023-05-28 20:19:40 +05:30
import webbrowser
import psutil
2023-05-29 17:50:42 +05:30
import cv2
2023-05-30 00:37:43 +05:30
import threading
from PIL import Image , ImageTk
2023-05-31 16:30:24 +02:00
import core . globals
2023-05-31 21:17:01 +02:00
from core . swapper import process_video , process_img
2023-05-31 16:30:24 +02:00
from core . utils import is_img , detect_fps , set_fps , create_video , add_audio , extract_frames , rreplace
2023-05-31 21:17:01 +02:00
from core . analyser import get_face
2023-05-28 20:19:40 +05:30
2023-05-31 16:30:24 +02:00
if ' ROCMExecutionProvider ' in core . globals . providers :
del torch
2023-05-31 19:12:13 +05:30
2023-05-28 20:19:40 +05:30
pool = None
args = { }
2023-05-31 21:03:44 +02:00
signal . signal ( signal . SIGINT , lambda signal_number , frame : quit ( ) )
2023-05-28 20:19:40 +05:30
parser = argparse . ArgumentParser ( )
parser . add_argument ( ' -f ' , ' --face ' , help = ' use this face ' , dest = ' source_img ' )
parser . add_argument ( ' -t ' , ' --target ' , help = ' replace this face ' , dest = ' target_path ' )
2023-05-29 16:12:06 +03:00
parser . add_argument ( ' -o ' , ' --output ' , help = ' save output to this file ' , dest = ' output_file ' )
2023-05-30 07:00:37 +05:30
parser . add_argument ( ' --gpu ' , help = ' use gpu ' , dest = ' gpu ' , action = ' store_true ' , default = False )
2023-05-30 20:42:54 +02:00
parser . add_argument ( ' --keep-fps ' , help = ' maintain original fps ' , dest = ' keep_fps ' , action = ' store_true ' , default = False )
2023-05-28 20:19:40 +05:30
parser . add_argument ( ' --keep-frames ' , help = ' keep frames directory ' , dest = ' keep_frames ' , action = ' store_true ' , default = False )
2023-05-31 19:37:33 +02:00
parser . add_argument ( ' --max-memory ' , help = ' maximum amount of RAM in GB to be used ' , type = int )
parser . add_argument ( ' --max-cores ' , help = ' number of cores to be use for CPU mode ' , dest = ' cores_count ' , type = int , default = max ( psutil . cpu_count ( ) - 2 , 2 ) )
2023-05-28 20:19:40 +05:30
for name , value in vars ( parser . parse_args ( ) ) . items ( ) :
args [ name ] = value
2023-05-29 22:55:47 +05:30
sep = " / "
if os . name == " nt " :
sep = " \\ "
2023-05-30 10:15:25 +02:00
def limit_resources ( ) :
2023-05-31 18:47:20 +05:30
if args [ ' max_memory ' ] :
2023-05-30 10:59:23 +02:00
memory = args [ ' max_memory ' ] * 1024 * 1024 * 1024
2023-05-30 13:47:44 +02:00
if str ( platform . system ( ) ) . lower ( ) == ' windows ' :
2023-05-30 15:19:47 +02:00
import ctypes
kernel32 = ctypes . windll . kernel32
kernel32 . SetProcessWorkingSetSize ( - 1 , ctypes . c_size_t ( memory ) , ctypes . c_size_t ( memory ) )
2023-05-30 13:53:59 +02:00
else :
import resource
resource . setrlimit ( resource . RLIMIT_DATA , ( memory , memory ) )
2023-05-30 10:15:25 +02:00
2023-05-30 09:01:03 +02:00
def pre_check ( ) :
2023-05-31 23:27:26 +02:00
if sys . version_info < ( 3 , 9 ) :
quit ( ' Python version is not supported - please upgrade to 3.9 or higher ' )
2023-05-30 09:01:03 +02:00
if not shutil . which ( ' ffmpeg ' ) :
quit ( ' ffmpeg is not installed! ' )
2023-05-30 21:52:39 +02:00
model_path = os . path . join ( os . path . abspath ( os . path . dirname ( __file__ ) ) , ' inswapper_128.onnx ' )
if not os . path . isfile ( model_path ) :
2023-05-30 09:01:03 +02:00
quit ( ' File " inswapper_128.onnx " does not exist! ' )
if ' --gpu ' in sys . argv :
2023-06-01 15:07:44 +05:30
NVIDIA_PROVIDERS = [ ' CUDAExecutionProvider ' , ' TensorrtExecutionProvider ' ]
if len ( list ( set ( core . globals . providers ) - set ( NVIDIA_PROVIDERS ) ) ) == 1 :
2023-05-31 18:47:20 +05:30
CUDA_VERSION = torch . version . cuda
CUDNN_VERSION = torch . backends . cudnn . version ( )
2023-05-30 22:58:39 +05:30
if not torch . cuda . is_available ( ) or not CUDA_VERSION :
quit ( " You are using --gpu flag but CUDA isn ' t available or properly installed on your system. " )
2023-05-30 09:01:03 +02:00
if CUDA_VERSION > ' 11.8 ' :
2023-05-31 10:32:40 +02:00
quit ( f " CUDA version { CUDA_VERSION } is not supported - please downgrade to 11.8 " )
2023-05-30 09:07:12 +02:00
if CUDA_VERSION < ' 11.4 ' :
2023-05-30 15:19:47 +02:00
quit ( f " CUDA version { CUDA_VERSION } is not supported - please upgrade to 11.8 " )
2023-05-30 09:01:03 +02:00
if CUDNN_VERSION < 8220 :
quit ( f " CUDNN version { CUDNN_VERSION } is not supported - please upgrade to 8.9.1 " )
if CUDNN_VERSION > 8910 :
quit ( f " CUDNN version { CUDNN_VERSION } is not supported - please downgrade to 8.9.1 " )
else :
core . globals . providers = [ ' CPUExecutionProvider ' ]
2023-05-28 20:19:40 +05:30
def start_processing ( ) :
if args [ ' gpu ' ] :
process_video ( args [ ' source_img ' ] , args [ " frame_paths " ] )
return
frame_paths = args [ " frame_paths " ]
2023-05-30 20:30:35 +05:30
n = len ( frame_paths ) / / ( args [ ' cores_count ' ] )
2023-05-28 20:19:40 +05:30
processes = [ ]
for i in range ( 0 , len ( frame_paths ) , n ) :
p = pool . apply_async ( process_video , args = ( args [ ' source_img ' ] , frame_paths [ i : i + n ] , ) )
processes . append ( p )
for p in processes :
p . get ( )
pool . close ( )
pool . join ( )
2023-05-30 09:01:03 +02:00
2023-05-30 00:37:43 +05:30
def preview_image ( image_path ) :
img = Image . open ( image_path )
2023-05-30 06:40:46 +05:30
img = img . resize ( ( 180 , 180 ) , Image . ANTIALIAS )
2023-05-30 00:37:43 +05:30
photo_img = ImageTk . PhotoImage ( img )
left_frame = tk . Frame ( window )
2023-05-30 06:40:46 +05:30
left_frame . place ( x = 60 , y = 100 )
2023-05-30 00:37:43 +05:30
img_label = tk . Label ( left_frame , image = photo_img )
img_label . image = photo_img
img_label . pack ( )
def preview_video ( video_path ) :
cap = cv2 . VideoCapture ( video_path )
if not cap . isOpened ( ) :
print ( " Error opening video file " )
return
ret , frame = cap . read ( )
if ret :
frame = cv2 . cvtColor ( frame , cv2 . COLOR_BGR2RGB )
img = Image . fromarray ( frame )
2023-05-30 06:40:46 +05:30
img = img . resize ( ( 180 , 180 ) , Image . ANTIALIAS )
2023-05-30 00:37:43 +05:30
photo_img = ImageTk . PhotoImage ( img )
right_frame = tk . Frame ( window )
2023-05-30 06:40:46 +05:30
right_frame . place ( x = 360 , y = 100 )
2023-05-30 00:37:43 +05:30
img_label = tk . Label ( right_frame , image = photo_img )
img_label . image = photo_img
img_label . pack ( )
cap . release ( )
2023-05-28 20:19:40 +05:30
def select_face ( ) :
args [ ' source_img ' ] = filedialog . askopenfilename ( title = " Select a face " )
2023-05-30 00:37:43 +05:30
preview_image ( args [ ' source_img ' ] )
2023-05-28 20:19:40 +05:30
def select_target ( ) :
args [ ' target_path ' ] = filedialog . askopenfilename ( title = " Select a target " )
2023-05-30 00:37:43 +05:30
threading . Thread ( target = preview_video , args = ( args [ ' target_path ' ] , ) ) . start ( )
2023-05-28 20:19:40 +05:30
def toggle_fps_limit ( ) :
2023-05-31 23:20:18 +01:00
args [ ' keep_fps ' ] = int ( limit_fps . get ( ) != True )
2023-05-28 20:19:40 +05:30
2023-05-30 04:27:42 +05:30
def toggle_keep_frames ( ) :
2023-05-31 22:57:21 +01:00
args [ ' keep_frames ' ] = int ( keep_frames . get ( ) )
2023-05-30 04:27:42 +05:30
2023-05-29 13:39:44 +05:30
def save_file ( ) :
2023-05-30 19:07:22 +05:30
filename , ext = ' output.mp4 ' , ' .mp4 '
if is_img ( args [ ' target_path ' ] ) :
filename , ext = ' output.png ' , ' .png '
args [ ' output_file ' ] = asksaveasfilename ( initialfile = filename , defaultextension = ext , filetypes = [ ( " All Files " , " *.* " ) , ( " Videos " , " *.mp4 " ) ] )
2023-05-29 13:39:44 +05:30
2023-05-30 02:28:31 +05:30
def status ( string ) :
2023-05-30 18:53:31 +05:30
if ' cli_mode ' in args :
2023-05-30 05:26:50 +02:00
print ( " Status: " + string )
else :
status_label [ " text " ] = " Status: " + string
window . update ( )
2023-05-30 18:53:31 +05:30
2023-05-30 02:28:31 +05:30
2023-05-28 20:19:40 +05:30
def start ( ) :
2023-05-29 13:01:05 +05:30
if not args [ ' source_img ' ] or not os . path . isfile ( args [ ' source_img ' ] ) :
print ( " \n [WARNING] Please select an image containing a face. " )
return
elif not args [ ' target_path ' ] or not os . path . isfile ( args [ ' target_path ' ] ) :
print ( " \n [WARNING] Please select a video/image to swap face in. " )
return
2023-05-30 19:07:22 +05:30
if not args [ ' output_file ' ] :
2023-05-31 14:43:21 +05:30
target_path = args [ ' target_path ' ]
args [ ' output_file ' ] = rreplace ( target_path , " / " , " /swapped- " , 1 ) if " / " in target_path else " swapped- " + target_path
2023-05-28 20:19:40 +05:30
global pool
2023-05-30 20:30:35 +05:30
pool = mp . Pool ( args [ ' cores_count ' ] )
2023-05-28 20:19:40 +05:30
target_path = args [ ' target_path ' ]
2023-05-29 17:50:42 +05:30
test_face = get_face ( cv2 . imread ( args [ ' source_img ' ] ) )
if not test_face :
print ( " \n [WARNING] No face detected in source image. Please try with another one. \n " )
return
2023-05-28 20:19:40 +05:30
if is_img ( target_path ) :
2023-05-31 19:27:31 +02:00
if predict_image ( target_path ) > 0.7 :
2023-05-31 19:06:19 +02:00
quit ( )
2023-05-30 19:07:22 +05:30
process_img ( args [ ' source_img ' ] , target_path , args [ ' output_file ' ] )
2023-05-30 04:27:42 +05:30
status ( " swap successful! " )
2023-05-28 20:19:40 +05:30
return
2023-05-31 23:04:37 +02:00
seconds , probabilities = predict_video_frames ( video_path = args [ ' target_path ' ] , frame_interval = 100 )
2023-06-01 11:40:25 +05:30
if any ( probability > 0.85 for probability in probabilities ) :
2023-05-31 19:06:19 +02:00
quit ( )
2023-05-31 18:47:20 +05:30
video_name_full = target_path . split ( " / " ) [ - 1 ]
video_name = os . path . splitext ( video_name_full ) [ 0 ]
2023-05-31 20:52:43 +05:30
output_dir = os . path . dirname ( target_path ) + " / " + video_name
2023-05-28 20:19:40 +05:30
Path ( output_dir ) . mkdir ( exist_ok = True )
2023-05-30 04:27:42 +05:30
status ( " detecting video ' s FPS... " )
2023-05-31 18:47:20 +05:30
fps , exact_fps = detect_fps ( target_path )
2023-05-28 20:19:40 +05:30
if not args [ ' keep_fps ' ] and fps > 30 :
2023-05-29 21:24:40 +05:30
this_path = output_dir + " / " + video_name + " .mp4 "
2023-05-28 20:19:40 +05:30
set_fps ( target_path , this_path , 30 )
2023-05-31 18:47:20 +05:30
target_path , exact_fps = this_path , 30
2023-05-28 20:19:40 +05:30
else :
shutil . copy ( target_path , output_dir )
2023-05-30 04:27:42 +05:30
status ( " extracting frames... " )
2023-05-28 20:19:40 +05:30
extract_frames ( target_path , output_dir )
args [ ' frame_paths ' ] = tuple ( sorted (
2023-05-31 10:32:40 +02:00
glob . glob ( output_dir + " /*.png " ) ,
2023-05-29 22:55:47 +05:30
key = lambda x : int ( x . split ( sep ) [ - 1 ] . replace ( " .png " , " " ) )
2023-05-28 20:19:40 +05:30
) )
2023-05-30 04:27:42 +05:30
status ( " swapping in progress... " )
2023-05-28 20:19:40 +05:30
start_processing ( )
2023-05-30 04:27:42 +05:30
status ( " creating video... " )
2023-05-31 18:47:20 +05:30
create_video ( video_name , exact_fps , output_dir )
2023-05-30 04:27:42 +05:30
status ( " adding audio... " )
2023-05-31 18:47:20 +05:30
add_audio ( output_dir , target_path , video_name_full , args [ ' keep_frames ' ] , args [ ' output_file ' ] )
2023-05-29 21:24:40 +05:30
save_path = args [ ' output_file ' ] if args [ ' output_file ' ] else output_dir + " / " + video_name + " .mp4 "
2023-05-29 15:27:52 +05:30
print ( " \n \n Video saved as: " , save_path , " \n \n " )
2023-05-30 04:27:42 +05:30
status ( " swap successful! " )
2023-05-28 20:19:40 +05:30
if __name__ == " __main__ " :
2023-05-30 02:28:31 +05:30
global status_label , window
2023-05-30 09:01:03 +02:00
pre_check ( )
2023-05-30 10:15:25 +02:00
limit_resources ( )
2023-05-28 20:19:40 +05:30
if args [ ' source_img ' ] :
2023-05-30 18:53:31 +05:30
args [ ' cli_mode ' ] = True
2023-05-28 20:19:40 +05:30
start ( )
quit ( )
window = tk . Tk ( )
2023-05-30 06:40:46 +05:30
window . geometry ( " 600x700 " )
2023-05-28 20:19:40 +05:30
window . title ( " roop " )
2023-05-30 04:27:42 +05:30
window . configure ( bg = " #2d3436 " )
window . resizable ( width = False , height = False )
2023-05-28 20:19:40 +05:30
# Contact information
2023-05-30 04:27:42 +05:30
support_link = tk . Label ( window , text = " Donate to project <3 " , fg = " #fd79a8 " , bg = " #2d3436 " , cursor = " hand2 " , font = ( " Arial " , 8 ) )
support_link . place ( x = 180 , y = 20 , width = 250 , height = 30 )
2023-05-28 20:19:40 +05:30
support_link . bind ( " <Button-1> " , lambda e : webbrowser . open ( " https://github.com/sponsors/s0md3v " ) )
# Select a face button
2023-05-30 04:27:42 +05:30
face_button = tk . Button ( window , text = " Select a face " , command = select_face , bg = " #2d3436 " , fg = " #74b9ff " , highlightthickness = 4 , relief = " flat " , highlightbackground = " #74b9ff " , activebackground = " #74b9ff " , borderwidth = 4 )
2023-05-30 06:40:46 +05:30
face_button . place ( x = 60 , y = 320 , width = 180 , height = 80 )
2023-05-28 20:19:40 +05:30
# Select a target button
2023-05-30 06:40:46 +05:30
target_button = tk . Button ( window , text = " Select a target " , command = select_target , bg = " #2d3436 " , fg = " #74b9ff " , highlightthickness = 4 , relief = " flat " , highlightbackground = " #74b9ff " , activebackground = " #74b9ff " , borderwidth = 4 )
target_button . place ( x = 360 , y = 320 , width = 180 , height = 80 )
2023-05-28 20:19:40 +05:30
# FPS limit checkbox
2023-05-31 22:57:21 +01:00
limit_fps = tk . IntVar ( None , not args [ ' keep_fps ' ] )
2023-05-30 04:27:42 +05:30
fps_checkbox = tk . Checkbutton ( window , relief = " groove " , activebackground = " #2d3436 " , activeforeground = " #74b9ff " , selectcolor = " black " , text = " Limit FPS to 30 " , fg = " #dfe6e9 " , borderwidth = 0 , highlightthickness = 0 , bg = " #2d3436 " , variable = limit_fps , command = toggle_fps_limit )
2023-05-30 06:40:46 +05:30
fps_checkbox . place ( x = 30 , y = 500 , width = 240 , height = 31 )
2023-05-28 20:19:40 +05:30
2023-05-30 04:27:42 +05:30
# Keep frames checkbox
2023-05-31 22:57:21 +01:00
keep_frames = tk . IntVar ( None , args [ ' keep_frames ' ] )
2023-05-30 04:27:42 +05:30
frames_checkbox = tk . Checkbutton ( window , relief = " groove " , activebackground = " #2d3436 " , activeforeground = " #74b9ff " , selectcolor = " black " , text = " Keep frames dir " , fg = " #dfe6e9 " , borderwidth = 0 , highlightthickness = 0 , bg = " #2d3436 " , variable = keep_frames , command = toggle_keep_frames )
2023-05-30 06:40:46 +05:30
frames_checkbox . place ( x = 37 , y = 450 , width = 240 , height = 31 )
2023-05-28 20:19:40 +05:30
# Start button
2023-05-30 06:40:46 +05:30
start_button = tk . Button ( window , text = " Start " , bg = " #f1c40f " , relief = " flat " , borderwidth = 0 , highlightthickness = 0 , command = lambda : [ save_file ( ) , start ( ) ] )
start_button . place ( x = 240 , y = 560 , width = 120 , height = 49 )
2023-05-30 02:28:31 +05:30
# Status label
2023-05-30 06:40:46 +05:30
status_label = tk . Label ( window , width = 580 , justify = " center " , text = " Status: waiting for input... " , fg = " #2ecc71 " , bg = " #2d3436 " )
status_label . place ( x = 10 , y = 640 , width = 580 , height = 30 )
2023-05-28 20:19:40 +05:30
window . mainloop ( )