Refactor application structure.
parent
6171141505
commit
b7d9889565
292
modules/core.py
292
modules/core.py
|
@ -17,7 +17,7 @@ import tensorflow
|
||||||
|
|
||||||
import modules.globals
|
import modules.globals
|
||||||
import modules.metadata
|
import modules.metadata
|
||||||
import modules.ui as ui
|
from modules.ui import DeepFakeUI
|
||||||
from modules.processors.frame.core import get_frame_processors_modules
|
from modules.processors.frame.core import get_frame_processors_modules
|
||||||
from modules.utilities import has_image_extension, is_image, is_video, detect_fps, create_video, extract_frames, get_temp_frame_paths, restore_audio, create_temp, move_temp, clean_temp, normalize_output_path
|
from modules.utilities import has_image_extension, is_image, is_video, detect_fps, create_video, extract_frames, get_temp_frame_paths, restore_audio, create_temp, move_temp, clean_temp, normalize_output_path
|
||||||
|
|
||||||
|
@ -27,9 +27,148 @@ if 'ROCMExecutionProvider' in modules.globals.execution_providers:
|
||||||
warnings.filterwarnings('ignore', category=FutureWarning, module='insightface')
|
warnings.filterwarnings('ignore', category=FutureWarning, module='insightface')
|
||||||
warnings.filterwarnings('ignore', category=UserWarning, module='torchvision')
|
warnings.filterwarnings('ignore', category=UserWarning, module='torchvision')
|
||||||
|
|
||||||
|
def encode_execution_providers(execution_providers: List[str]) -> List[str]:
|
||||||
|
return [execution_provider.replace('ExecutionProvider', '').lower() for execution_provider in execution_providers]
|
||||||
|
|
||||||
def parse_args() -> None:
|
|
||||||
signal.signal(signal.SIGINT, lambda signal_number, frame: destroy())
|
def decode_execution_providers(execution_providers: List[str]) -> List[str]:
|
||||||
|
return [provider for provider, encoded_execution_provider in zip(onnxruntime.get_available_providers(), encode_execution_providers(onnxruntime.get_available_providers()))
|
||||||
|
if any(execution_provider in encoded_execution_provider for execution_provider in execution_providers)]
|
||||||
|
|
||||||
|
|
||||||
|
def suggest_max_memory() -> int:
|
||||||
|
if platform.system().lower() == 'darwin':
|
||||||
|
return 4
|
||||||
|
return 16
|
||||||
|
|
||||||
|
|
||||||
|
def suggest_execution_providers() -> List[str]:
|
||||||
|
return encode_execution_providers(onnxruntime.get_available_providers())
|
||||||
|
|
||||||
|
|
||||||
|
def suggest_execution_threads() -> int:
|
||||||
|
if 'DmlExecutionProvider' in modules.globals.execution_providers:
|
||||||
|
return 1
|
||||||
|
if 'ROCMExecutionProvider' in modules.globals.execution_providers:
|
||||||
|
return 1
|
||||||
|
return 8
|
||||||
|
|
||||||
|
|
||||||
|
def limit_resources() -> None:
|
||||||
|
# prevent tensorflow memory leak
|
||||||
|
gpus = tensorflow.config.experimental.list_physical_devices('GPU')
|
||||||
|
for gpu in gpus:
|
||||||
|
tensorflow.config.experimental.set_memory_growth(gpu, True)
|
||||||
|
# limit memory usage
|
||||||
|
if modules.globals.max_memory:
|
||||||
|
memory = modules.globals.max_memory * 1024 ** 3
|
||||||
|
if platform.system().lower() == 'darwin':
|
||||||
|
memory = modules.globals.max_memory * 1024 ** 6
|
||||||
|
if platform.system().lower() == 'windows':
|
||||||
|
import ctypes
|
||||||
|
kernel32 = ctypes.windll.kernel32
|
||||||
|
kernel32.SetProcessWorkingSetSize(-1, ctypes.c_size_t(memory), ctypes.c_size_t(memory))
|
||||||
|
else:
|
||||||
|
import resource
|
||||||
|
resource.setrlimit(resource.RLIMIT_DATA, (memory, memory))
|
||||||
|
|
||||||
|
|
||||||
|
def release_resources() -> None:
|
||||||
|
if 'CUDAExecutionProvider' in modules.globals.execution_providers:
|
||||||
|
torch.cuda.empty_cache()
|
||||||
|
|
||||||
|
class DeepFakeApp:
|
||||||
|
def __init__(self):
|
||||||
|
self.ui = DeepFakeUI(
|
||||||
|
self.start,
|
||||||
|
self.destroy
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_status(self, message: str, scope: str = 'DLC.CORE') -> None:
|
||||||
|
print(f'[{scope}] {message}')
|
||||||
|
if not modules.globals.headless:
|
||||||
|
self.ui.update_status(message)
|
||||||
|
|
||||||
|
def pre_check(self) -> bool:
|
||||||
|
if sys.version_info < (3, 9):
|
||||||
|
update_status('Python version is not supported - please upgrade to 3.9 or higher.')
|
||||||
|
return False
|
||||||
|
if not shutil.which('ffmpeg'):
|
||||||
|
self.update_status('ffmpeg is not installed.')
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def start(self) -> None:
|
||||||
|
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
|
||||||
|
if not frame_processor.pre_start():
|
||||||
|
return
|
||||||
|
self.update_status('Processing...')
|
||||||
|
# process image to image
|
||||||
|
if has_image_extension(modules.globals.target_path):
|
||||||
|
if modules.globals.nsfw_filter and self.ui.check_and_ignore_nsfw(modules.globals.target_path, self.destroy):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
shutil.copy2(modules.globals.target_path, modules.globals.output_path)
|
||||||
|
except Exception as e:
|
||||||
|
print("Error copying file:", str(e))
|
||||||
|
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
|
||||||
|
self.update_status('Progressing...', frame_processor.NAME)
|
||||||
|
frame_processor.process_image(modules.globals.source_path, modules.globals.output_path, modules.globals.output_path)
|
||||||
|
release_resources()
|
||||||
|
if is_image(modules.globals.target_path):
|
||||||
|
self.update_status('Processing to image succeed!')
|
||||||
|
else:
|
||||||
|
self.update_status('Processing to image failed!')
|
||||||
|
return
|
||||||
|
# process image to videos
|
||||||
|
if modules.globals.nsfw_filter and self.ui.check_and_ignore_nsfw(modules.globals.target_path, self.destroy):
|
||||||
|
return
|
||||||
|
|
||||||
|
if not modules.globals.map_faces:
|
||||||
|
self.update_status('Creating temp resources...')
|
||||||
|
create_temp(modules.globals.target_path)
|
||||||
|
self.update_status('Extracting frames...')
|
||||||
|
extract_frames(modules.globals.target_path)
|
||||||
|
|
||||||
|
temp_frame_paths = get_temp_frame_paths(modules.globals.target_path)
|
||||||
|
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
|
||||||
|
self.update_status('Progressing...', frame_processor.NAME)
|
||||||
|
frame_processor.process_video(modules.globals.source_path, temp_frame_paths)
|
||||||
|
release_resources()
|
||||||
|
# handles fps
|
||||||
|
if modules.globals.keep_fps:
|
||||||
|
self.update_status('Detecting fps...')
|
||||||
|
fps = detect_fps(modules.globals.target_path)
|
||||||
|
self.update_status(f'Creating video with {fps} fps...')
|
||||||
|
create_video(modules.globals.target_path, fps)
|
||||||
|
else:
|
||||||
|
self.update_status('Creating video with 30.0 fps...')
|
||||||
|
create_video(modules.globals.target_path)
|
||||||
|
# handle audio
|
||||||
|
if modules.globals.keep_audio:
|
||||||
|
if modules.globals.keep_fps:
|
||||||
|
self.update_status('Restoring audio...')
|
||||||
|
else:
|
||||||
|
self.update_status('Restoring audio might cause issues as fps are not kept...')
|
||||||
|
restore_audio(modules.globals.target_path, modules.globals.output_path)
|
||||||
|
else:
|
||||||
|
move_temp(modules.globals.target_path, modules.globals.output_path)
|
||||||
|
# clean and validate
|
||||||
|
clean_temp(modules.globals.target_path)
|
||||||
|
if is_video(modules.globals.target_path):
|
||||||
|
self.update_status('Processing to video succeed!')
|
||||||
|
else:
|
||||||
|
self.update_status('Processing to video failed!')
|
||||||
|
|
||||||
|
|
||||||
|
def destroy(self, to_quit=True) -> None:
|
||||||
|
if modules.globals.target_path:
|
||||||
|
clean_temp(modules.globals.target_path)
|
||||||
|
if to_quit:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
def parse_args(self) -> None:
|
||||||
|
signal.signal(signal.SIGINT, lambda _: self.destroy())
|
||||||
program = argparse.ArgumentParser()
|
program = argparse.ArgumentParser()
|
||||||
program.add_argument('-s', '--source', help='select an source image', dest='source_path')
|
program.add_argument('-s', '--source', help='select an source image', dest='source_path')
|
||||||
program.add_argument('-t', '--target', help='select an target image or video', dest='target_path')
|
program.add_argument('-t', '--target', help='select an target image or video', dest='target_path')
|
||||||
|
@ -105,151 +244,16 @@ def parse_args() -> None:
|
||||||
modules.globals.execution_threads = args.gpu_threads_deprecated
|
modules.globals.execution_threads = args.gpu_threads_deprecated
|
||||||
|
|
||||||
|
|
||||||
def encode_execution_providers(execution_providers: List[str]) -> List[str]:
|
def run(self) -> None:
|
||||||
return [execution_provider.replace('ExecutionProvider', '').lower() for execution_provider in execution_providers]
|
self.parse_args()
|
||||||
|
if not self.pre_check():
|
||||||
|
|
||||||
def decode_execution_providers(execution_providers: List[str]) -> List[str]:
|
|
||||||
return [provider for provider, encoded_execution_provider in zip(onnxruntime.get_available_providers(), encode_execution_providers(onnxruntime.get_available_providers()))
|
|
||||||
if any(execution_provider in encoded_execution_provider for execution_provider in execution_providers)]
|
|
||||||
|
|
||||||
|
|
||||||
def suggest_max_memory() -> int:
|
|
||||||
if platform.system().lower() == 'darwin':
|
|
||||||
return 4
|
|
||||||
return 16
|
|
||||||
|
|
||||||
|
|
||||||
def suggest_execution_providers() -> List[str]:
|
|
||||||
return encode_execution_providers(onnxruntime.get_available_providers())
|
|
||||||
|
|
||||||
|
|
||||||
def suggest_execution_threads() -> int:
|
|
||||||
if 'DmlExecutionProvider' in modules.globals.execution_providers:
|
|
||||||
return 1
|
|
||||||
if 'ROCMExecutionProvider' in modules.globals.execution_providers:
|
|
||||||
return 1
|
|
||||||
return 8
|
|
||||||
|
|
||||||
|
|
||||||
def limit_resources() -> None:
|
|
||||||
# prevent tensorflow memory leak
|
|
||||||
gpus = tensorflow.config.experimental.list_physical_devices('GPU')
|
|
||||||
for gpu in gpus:
|
|
||||||
tensorflow.config.experimental.set_memory_growth(gpu, True)
|
|
||||||
# limit memory usage
|
|
||||||
if modules.globals.max_memory:
|
|
||||||
memory = modules.globals.max_memory * 1024 ** 3
|
|
||||||
if platform.system().lower() == 'darwin':
|
|
||||||
memory = modules.globals.max_memory * 1024 ** 6
|
|
||||||
if platform.system().lower() == 'windows':
|
|
||||||
import ctypes
|
|
||||||
kernel32 = ctypes.windll.kernel32
|
|
||||||
kernel32.SetProcessWorkingSetSize(-1, ctypes.c_size_t(memory), ctypes.c_size_t(memory))
|
|
||||||
else:
|
|
||||||
import resource
|
|
||||||
resource.setrlimit(resource.RLIMIT_DATA, (memory, memory))
|
|
||||||
|
|
||||||
|
|
||||||
def release_resources() -> None:
|
|
||||||
if 'CUDAExecutionProvider' in modules.globals.execution_providers:
|
|
||||||
torch.cuda.empty_cache()
|
|
||||||
|
|
||||||
|
|
||||||
def pre_check() -> bool:
|
|
||||||
if sys.version_info < (3, 9):
|
|
||||||
update_status('Python version is not supported - please upgrade to 3.9 or higher.')
|
|
||||||
return False
|
|
||||||
if not shutil.which('ffmpeg'):
|
|
||||||
update_status('ffmpeg is not installed.')
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def update_status(message: str, scope: str = 'DLC.CORE') -> None:
|
|
||||||
print(f'[{scope}] {message}')
|
|
||||||
if not modules.globals.headless:
|
|
||||||
ui.update_status(message)
|
|
||||||
|
|
||||||
def start() -> None:
|
|
||||||
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
|
|
||||||
if not frame_processor.pre_start():
|
|
||||||
return
|
|
||||||
update_status('Processing...')
|
|
||||||
# process image to image
|
|
||||||
if has_image_extension(modules.globals.target_path):
|
|
||||||
if modules.globals.nsfw_filter and ui.check_and_ignore_nsfw(modules.globals.target_path, destroy):
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
shutil.copy2(modules.globals.target_path, modules.globals.output_path)
|
|
||||||
except Exception as e:
|
|
||||||
print("Error copying file:", str(e))
|
|
||||||
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
|
|
||||||
update_status('Progressing...', frame_processor.NAME)
|
|
||||||
frame_processor.process_image(modules.globals.source_path, modules.globals.output_path, modules.globals.output_path)
|
|
||||||
release_resources()
|
|
||||||
if is_image(modules.globals.target_path):
|
|
||||||
update_status('Processing to image succeed!')
|
|
||||||
else:
|
|
||||||
update_status('Processing to image failed!')
|
|
||||||
return
|
|
||||||
# process image to videos
|
|
||||||
if modules.globals.nsfw_filter and ui.check_and_ignore_nsfw(modules.globals.target_path, destroy):
|
|
||||||
return
|
|
||||||
|
|
||||||
if not modules.globals.map_faces:
|
|
||||||
update_status('Creating temp resources...')
|
|
||||||
create_temp(modules.globals.target_path)
|
|
||||||
update_status('Extracting frames...')
|
|
||||||
extract_frames(modules.globals.target_path)
|
|
||||||
|
|
||||||
temp_frame_paths = get_temp_frame_paths(modules.globals.target_path)
|
|
||||||
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
|
|
||||||
update_status('Progressing...', frame_processor.NAME)
|
|
||||||
frame_processor.process_video(modules.globals.source_path, temp_frame_paths)
|
|
||||||
release_resources()
|
|
||||||
# handles fps
|
|
||||||
if modules.globals.keep_fps:
|
|
||||||
update_status('Detecting fps...')
|
|
||||||
fps = detect_fps(modules.globals.target_path)
|
|
||||||
update_status(f'Creating video with {fps} fps...')
|
|
||||||
create_video(modules.globals.target_path, fps)
|
|
||||||
else:
|
|
||||||
update_status('Creating video with 30.0 fps...')
|
|
||||||
create_video(modules.globals.target_path)
|
|
||||||
# handle audio
|
|
||||||
if modules.globals.keep_audio:
|
|
||||||
if modules.globals.keep_fps:
|
|
||||||
update_status('Restoring audio...')
|
|
||||||
else:
|
|
||||||
update_status('Restoring audio might cause issues as fps are not kept...')
|
|
||||||
restore_audio(modules.globals.target_path, modules.globals.output_path)
|
|
||||||
else:
|
|
||||||
move_temp(modules.globals.target_path, modules.globals.output_path)
|
|
||||||
# clean and validate
|
|
||||||
clean_temp(modules.globals.target_path)
|
|
||||||
if is_video(modules.globals.target_path):
|
|
||||||
update_status('Processing to video succeed!')
|
|
||||||
else:
|
|
||||||
update_status('Processing to video failed!')
|
|
||||||
|
|
||||||
|
|
||||||
def destroy(to_quit=True) -> None:
|
|
||||||
if modules.globals.target_path:
|
|
||||||
clean_temp(modules.globals.target_path)
|
|
||||||
if to_quit: quit()
|
|
||||||
|
|
||||||
|
|
||||||
def run() -> None:
|
|
||||||
parse_args()
|
|
||||||
if not pre_check():
|
|
||||||
return
|
return
|
||||||
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
|
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
|
||||||
if not frame_processor.pre_check():
|
if not frame_processor.pre_check():
|
||||||
return
|
return
|
||||||
limit_resources()
|
limit_resources()
|
||||||
if modules.globals.headless:
|
if modules.globals.headless:
|
||||||
start()
|
self.start()
|
||||||
else:
|
else:
|
||||||
window = ui.init(start, destroy)
|
self.ui.root.mainloop()
|
||||||
window.mainloop()
|
|
||||||
|
|
|
@ -19,14 +19,10 @@ FRAME_PROCESSORS_INTERFACE = [
|
||||||
|
|
||||||
|
|
||||||
def load_frame_processor_module(frame_processor: str) -> Any:
|
def load_frame_processor_module(frame_processor: str) -> Any:
|
||||||
try:
|
|
||||||
frame_processor_module = importlib.import_module(f'modules.processors.frame.{frame_processor}')
|
frame_processor_module = importlib.import_module(f'modules.processors.frame.{frame_processor}')
|
||||||
for method_name in FRAME_PROCESSORS_INTERFACE:
|
for method_name in FRAME_PROCESSORS_INTERFACE:
|
||||||
if not hasattr(frame_processor_module, method_name):
|
if not hasattr(frame_processor_module, method_name):
|
||||||
sys.exit()
|
sys.exit()
|
||||||
except ImportError:
|
|
||||||
print(f"Frame processor {frame_processor} not found")
|
|
||||||
sys.exit()
|
|
||||||
return frame_processor_module
|
return frame_processor_module
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import threading
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import modules.globals
|
import modules.globals
|
||||||
import modules.processors.frame.core
|
import modules.processors.frame.core
|
||||||
from modules.core import update_status
|
from run import app
|
||||||
from modules.face_analyser import get_one_face, get_many_faces, default_source_face
|
from modules.face_analyser import get_one_face, get_many_faces, default_source_face
|
||||||
from modules.typing import Face, Frame
|
from modules.typing import Face, Frame
|
||||||
from modules.utilities import (
|
from modules.utilities import (
|
||||||
|
@ -36,17 +36,17 @@ def pre_check() -> bool:
|
||||||
|
|
||||||
def pre_start() -> bool:
|
def pre_start() -> bool:
|
||||||
if not modules.globals.map_faces and not is_image(modules.globals.source_path):
|
if not modules.globals.map_faces and not is_image(modules.globals.source_path):
|
||||||
update_status("Select an image for source path.", NAME)
|
app.update_status("Select an image for source path.", NAME)
|
||||||
return False
|
return False
|
||||||
elif not modules.globals.map_faces and not get_one_face(
|
elif not modules.globals.map_faces and not get_one_face(
|
||||||
cv2.imread(modules.globals.source_path)
|
cv2.imread(modules.globals.source_path)
|
||||||
):
|
):
|
||||||
update_status("No face in source path detected.", NAME)
|
app.update_status("No face in source path detected.", NAME)
|
||||||
return False
|
return False
|
||||||
if not is_image(modules.globals.target_path) and not is_video(
|
if not is_image(modules.globals.target_path) and not is_video(
|
||||||
modules.globals.target_path
|
modules.globals.target_path
|
||||||
):
|
):
|
||||||
update_status("Select an image or video for target path.", NAME)
|
app.update_status("Select an image or video for target path.", NAME)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ def process_image(source_path: str, target_path: str, output_path: str) -> None:
|
||||||
cv2.imwrite(output_path, result)
|
cv2.imwrite(output_path, result)
|
||||||
else:
|
else:
|
||||||
if modules.globals.many_faces:
|
if modules.globals.many_faces:
|
||||||
update_status(
|
app.update_status(
|
||||||
"Many faces enabled. Using first source image. Progressing...", NAME
|
"Many faces enabled. Using first source image. Progressing...", NAME
|
||||||
)
|
)
|
||||||
target_frame = cv2.imread(output_path)
|
target_frame = cv2.imread(output_path)
|
||||||
|
@ -246,7 +246,7 @@ def process_image(source_path: str, target_path: str, output_path: str) -> None:
|
||||||
|
|
||||||
def process_video(source_path: str, temp_frame_paths: List[str]) -> None:
|
def process_video(source_path: str, temp_frame_paths: List[str]) -> None:
|
||||||
if modules.globals.map_faces and modules.globals.many_faces:
|
if modules.globals.map_faces and modules.globals.many_faces:
|
||||||
update_status(
|
app.update_status(
|
||||||
"Many faces enabled. Using first source image. Progressing...", NAME
|
"Many faces enabled. Using first source image. Progressing...", NAME
|
||||||
)
|
)
|
||||||
modules.processors.frame.core.process_video(
|
modules.processors.frame.core.process_video(
|
||||||
|
@ -256,7 +256,7 @@ def process_video(source_path: str, temp_frame_paths: List[str]) -> None:
|
||||||
|
|
||||||
def create_lower_mouth_mask(
|
def create_lower_mouth_mask(
|
||||||
face: Face, frame: Frame
|
face: Face, frame: Frame
|
||||||
) -> (np.ndarray, np.ndarray, tuple, np.ndarray):
|
) -> tuple[np.ndarray, np.ndarray, tuple, np.ndarray]:
|
||||||
mask = np.zeros(frame.shape[:2], dtype=np.uint8)
|
mask = np.zeros(frame.shape[:2], dtype=np.uint8)
|
||||||
mouth_cutout = None
|
mouth_cutout = None
|
||||||
landmarks = face.landmark_2d_106
|
landmarks = face.landmark_2d_106
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from typing import Any
|
from typing import Any, TypeAlias
|
||||||
|
|
||||||
from insightface.app.common import Face
|
from insightface.app.common import Face
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
Face = Face
|
Frame: TypeAlias = numpy.ndarray[Any, Any]
|
||||||
Frame = numpy.ndarray[Any, Any]
|
|
||||||
|
|
446
modules/ui.py
446
modules/ui.py
|
@ -7,6 +7,8 @@ from cv2_enumerate_cameras import enumerate_cameras # Add this import
|
||||||
from PIL import Image, ImageOps
|
from PIL import Image, ImageOps
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
|
from numpy import ndarray
|
||||||
|
from modules.predicter import predict_image, predict_video, predict_frame
|
||||||
|
|
||||||
import modules.globals
|
import modules.globals
|
||||||
import modules.metadata
|
import modules.metadata
|
||||||
|
@ -27,13 +29,9 @@ from modules.utilities import (
|
||||||
has_image_extension,
|
has_image_extension,
|
||||||
)
|
)
|
||||||
|
|
||||||
ROOT = None
|
|
||||||
POPUP = None
|
|
||||||
POPUP_LIVE = None
|
|
||||||
ROOT_HEIGHT = 700
|
ROOT_HEIGHT = 700
|
||||||
ROOT_WIDTH = 600
|
ROOT_WIDTH = 600
|
||||||
|
|
||||||
PREVIEW = None
|
|
||||||
PREVIEW_MAX_HEIGHT = 700
|
PREVIEW_MAX_HEIGHT = 700
|
||||||
PREVIEW_MAX_WIDTH = 1200
|
PREVIEW_MAX_WIDTH = 1200
|
||||||
PREVIEW_DEFAULT_WIDTH = 960
|
PREVIEW_DEFAULT_WIDTH = 960
|
||||||
|
@ -55,33 +53,9 @@ MAPPER_PREVIEW_MAX_WIDTH = 100
|
||||||
DEFAULT_BUTTON_WIDTH = 200
|
DEFAULT_BUTTON_WIDTH = 200
|
||||||
DEFAULT_BUTTON_HEIGHT = 40
|
DEFAULT_BUTTON_HEIGHT = 40
|
||||||
|
|
||||||
RECENT_DIRECTORY_SOURCE = None
|
|
||||||
RECENT_DIRECTORY_TARGET = None
|
|
||||||
RECENT_DIRECTORY_OUTPUT = None
|
|
||||||
|
|
||||||
preview_label = None
|
|
||||||
preview_slider = None
|
|
||||||
source_label = None
|
|
||||||
target_label = None
|
|
||||||
status_label = None
|
|
||||||
popup_status_label = None
|
|
||||||
popup_status_label_live = None
|
|
||||||
source_label_dict = {}
|
|
||||||
source_label_dict_live = {}
|
|
||||||
target_label_dict_live = {}
|
|
||||||
|
|
||||||
img_ft, vid_ft = modules.globals.file_types
|
img_ft, vid_ft = modules.globals.file_types
|
||||||
|
|
||||||
|
|
||||||
def init(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk:
|
|
||||||
global ROOT, PREVIEW
|
|
||||||
|
|
||||||
ROOT = create_root(start, destroy)
|
|
||||||
PREVIEW = create_preview(ROOT)
|
|
||||||
|
|
||||||
return ROOT
|
|
||||||
|
|
||||||
|
|
||||||
def save_switch_states():
|
def save_switch_states():
|
||||||
switch_states = {
|
switch_states = {
|
||||||
"keep_fps": modules.globals.keep_fps,
|
"keep_fps": modules.globals.keep_fps,
|
||||||
|
@ -123,10 +97,58 @@ def load_switch_states():
|
||||||
# If the file doesn't exist, use default values
|
# If the file doesn't exist, use default values
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def fit_image_to_size(image, width: int, height: int):
|
||||||
|
if width is None and height is None:
|
||||||
|
return image
|
||||||
|
h, w, _ = image.shape
|
||||||
|
ratio_h = 0.0
|
||||||
|
ratio_w = 0.0
|
||||||
|
if width > height:
|
||||||
|
ratio_h = height / h
|
||||||
|
else:
|
||||||
|
ratio_w = width / w
|
||||||
|
ratio = max(ratio_w, ratio_h)
|
||||||
|
new_size = (int(ratio * w), int(ratio * h))
|
||||||
|
return cv2.resize(image, dsize=new_size)
|
||||||
|
|
||||||
def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk:
|
def get_available_cameras():
|
||||||
global source_label, target_label, status_label, show_fps_switch
|
"""Returns a list of available camera names and indices."""
|
||||||
|
camera_indices = []
|
||||||
|
camera_names = []
|
||||||
|
|
||||||
|
for camera in enumerate_cameras():
|
||||||
|
cap = cv2.VideoCapture(camera.index)
|
||||||
|
if cap.isOpened():
|
||||||
|
camera_indices.append(camera.index)
|
||||||
|
camera_names.append(camera.name)
|
||||||
|
cap.release()
|
||||||
|
return (camera_indices, camera_names)
|
||||||
|
|
||||||
|
class DeepFakeUI:
|
||||||
|
preview_label: ctk.CTkLabel
|
||||||
|
source_label: ctk.CTkLabel
|
||||||
|
target_label: ctk.CTkLabel
|
||||||
|
status_label: ctk.CTkLabel
|
||||||
|
popup_status_label: ctk.CTkLabel
|
||||||
|
popup_status_label_live: ctk.CTkLabel
|
||||||
|
preview_slider: ctk.CTkSlider
|
||||||
|
source_label_dict: dict[int, ctk.CTkLabel] = {}
|
||||||
|
source_label_dict_live: dict[int, ctk.CTkLabel] = {}
|
||||||
|
target_label_dict_live: dict[int, ctk.CTkLabel] = {}
|
||||||
|
source_label_dict_live = {}
|
||||||
|
target_label_dict_live = {}
|
||||||
|
popup_live: ctk.CTkToplevel
|
||||||
|
popup: ctk.CTkToplevel = None
|
||||||
|
|
||||||
|
recent_directory_source: str = os.path.expanduser("~")
|
||||||
|
recent_directory_target: str = os.path.expanduser("~")
|
||||||
|
recent_directory_output: str = os.path.expanduser("~")
|
||||||
|
|
||||||
|
def __init__(self, start: Callable[[], None], destroy: Callable[[], None]) -> None:
|
||||||
|
self.root = self.create_root(start, destroy)
|
||||||
|
self.preview = self.create_preview(self.root)
|
||||||
|
|
||||||
|
def create_root(self, start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk:
|
||||||
load_switch_states()
|
load_switch_states()
|
||||||
|
|
||||||
ctk.deactivate_automatic_dpi_awareness()
|
ctk.deactivate_automatic_dpi_awareness()
|
||||||
|
@ -148,12 +170,12 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||||
target_label.place(relx=0.6, rely=0.1, relwidth=0.3, relheight=0.25)
|
target_label.place(relx=0.6, rely=0.1, relwidth=0.3, relheight=0.25)
|
||||||
|
|
||||||
select_face_button = ctk.CTkButton(
|
select_face_button = ctk.CTkButton(
|
||||||
root, text="Select a face", cursor="hand2", command=lambda: select_source_path()
|
root, text="Select a face", cursor="hand2", command=lambda: self.select_source_path()
|
||||||
)
|
)
|
||||||
select_face_button.place(relx=0.1, rely=0.4, relwidth=0.3, relheight=0.1)
|
select_face_button.place(relx=0.1, rely=0.4, relwidth=0.3, relheight=0.1)
|
||||||
|
|
||||||
swap_faces_button = ctk.CTkButton(
|
swap_faces_button = ctk.CTkButton(
|
||||||
root, text="↔", cursor="hand2", command=lambda: swap_faces_paths()
|
root, text="↔", cursor="hand2", command=lambda: self.swap_faces_paths()
|
||||||
)
|
)
|
||||||
swap_faces_button.place(relx=0.45, rely=0.4, relwidth=0.1, relheight=0.1)
|
swap_faces_button.place(relx=0.45, rely=0.4, relwidth=0.1, relheight=0.1)
|
||||||
|
|
||||||
|
@ -161,7 +183,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||||
root,
|
root,
|
||||||
text="Select a target",
|
text="Select a target",
|
||||||
cursor="hand2",
|
cursor="hand2",
|
||||||
command=lambda: select_target_path(),
|
command=lambda: self.select_target_path(),
|
||||||
)
|
)
|
||||||
select_target_button.place(relx=0.6, rely=0.4, relwidth=0.3, relheight=0.1)
|
select_target_button.place(relx=0.6, rely=0.4, relwidth=0.3, relheight=0.1)
|
||||||
|
|
||||||
|
@ -198,7 +220,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||||
variable=enhancer_value,
|
variable=enhancer_value,
|
||||||
cursor="hand2",
|
cursor="hand2",
|
||||||
command=lambda: (
|
command=lambda: (
|
||||||
update_tumbler("face_enhancer", enhancer_value.get()),
|
self.update_tumbler("face_enhancer", enhancer_value.get()),
|
||||||
save_switch_states(),
|
save_switch_states(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -296,7 +318,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||||
show_mouth_mask_box_switch.place(relx=0.6, rely=0.55)
|
show_mouth_mask_box_switch.place(relx=0.6, rely=0.55)
|
||||||
|
|
||||||
start_button = ctk.CTkButton(
|
start_button = ctk.CTkButton(
|
||||||
root, text="Start", cursor="hand2", command=lambda: analyze_target(start, root)
|
root, text="Start", cursor="hand2", command=lambda: self.analyze_target(start, root)
|
||||||
)
|
)
|
||||||
start_button.place(relx=0.15, rely=0.80, relwidth=0.2, relheight=0.05)
|
start_button.place(relx=0.15, rely=0.80, relwidth=0.2, relheight=0.05)
|
||||||
|
|
||||||
|
@ -306,7 +328,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||||
stop_button.place(relx=0.4, rely=0.80, relwidth=0.2, relheight=0.05)
|
stop_button.place(relx=0.4, rely=0.80, relwidth=0.2, relheight=0.05)
|
||||||
|
|
||||||
preview_button = ctk.CTkButton(
|
preview_button = ctk.CTkButton(
|
||||||
root, text="Preview", cursor="hand2", command=lambda: toggle_preview()
|
root, text="Preview", cursor="hand2", command=lambda: self.toggle_preview()
|
||||||
)
|
)
|
||||||
preview_button.place(relx=0.65, rely=0.80, relwidth=0.2, relheight=0.05)
|
preview_button.place(relx=0.65, rely=0.80, relwidth=0.2, relheight=0.05)
|
||||||
|
|
||||||
|
@ -333,7 +355,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||||
root,
|
root,
|
||||||
text="Live",
|
text="Live",
|
||||||
cursor="hand2",
|
cursor="hand2",
|
||||||
command=lambda: webcam_preview(
|
command=lambda: self.webcam_preview(
|
||||||
root,
|
root,
|
||||||
available_camera_indices[
|
available_camera_indices[
|
||||||
available_camera_strings.index(camera_variable.get())
|
available_camera_strings.index(camera_variable.get())
|
||||||
|
@ -357,56 +379,58 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||||
"<Button>", lambda event: webbrowser.open("https://paypal.me/hacksider")
|
"<Button>", lambda event: webbrowser.open("https://paypal.me/hacksider")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.source_label = source_label
|
||||||
|
self.target_label = target_label
|
||||||
|
self.status_label = status_label
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
|
||||||
def analyze_target(start: Callable[[], None], root: ctk.CTk):
|
def analyze_target(self, start: Callable[[], None], root: ctk.CTk):
|
||||||
if POPUP != None and POPUP.winfo_exists():
|
if self.popup != None and self.popup.winfo_exists():
|
||||||
update_status("Please complete pop-up or close it.")
|
self.update_status("Please complete pop-up or close it.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if modules.globals.map_faces:
|
if modules.globals.map_faces:
|
||||||
modules.globals.souce_target_map = []
|
modules.globals.souce_target_map = []
|
||||||
|
|
||||||
if is_image(modules.globals.target_path):
|
if is_image(modules.globals.target_path):
|
||||||
update_status("Getting unique faces")
|
self.update_status("Getting unique faces")
|
||||||
get_unique_faces_from_target_image()
|
get_unique_faces_from_target_image()
|
||||||
elif is_video(modules.globals.target_path):
|
elif is_video(modules.globals.target_path):
|
||||||
update_status("Getting unique faces")
|
self.update_status("Getting unique faces")
|
||||||
get_unique_faces_from_target_video()
|
get_unique_faces_from_target_video()
|
||||||
|
|
||||||
if len(modules.globals.souce_target_map) > 0:
|
if len(modules.globals.souce_target_map) > 0:
|
||||||
create_source_target_popup(start, root, modules.globals.souce_target_map)
|
self.create_source_target_popup(start, root, modules.globals.souce_target_map)
|
||||||
else:
|
else:
|
||||||
update_status("No faces found in target")
|
self.update_status("No faces found in target")
|
||||||
else:
|
else:
|
||||||
select_output_path(start)
|
self.select_output_path(start)
|
||||||
|
|
||||||
|
|
||||||
def create_source_target_popup(
|
def create_source_target_popup(
|
||||||
start: Callable[[], None], root: ctk.CTk, map: list
|
self, start: Callable[[], None], root: ctk.CTk, map: list
|
||||||
) -> None:
|
) -> None:
|
||||||
global POPUP, popup_status_label
|
popup = ctk.CTkToplevel(root)
|
||||||
|
popup.title("Source x Target Mapper")
|
||||||
POPUP = ctk.CTkToplevel(root)
|
popup.geometry(f"{POPUP_WIDTH}x{POPUP_HEIGHT}")
|
||||||
POPUP.title("Source x Target Mapper")
|
popup.focus()
|
||||||
POPUP.geometry(f"{POPUP_WIDTH}x{POPUP_HEIGHT}")
|
|
||||||
POPUP.focus()
|
|
||||||
|
|
||||||
def on_submit_click(start):
|
def on_submit_click(start):
|
||||||
if has_valid_map():
|
if has_valid_map():
|
||||||
POPUP.destroy()
|
popup.destroy()
|
||||||
select_output_path(start)
|
self.select_output_path(start)
|
||||||
else:
|
else:
|
||||||
update_pop_status("Atleast 1 source with target is required!")
|
self.update_pop_status("Atleast 1 source with target is required!")
|
||||||
|
|
||||||
scrollable_frame = ctk.CTkScrollableFrame(
|
scrollable_frame = ctk.CTkScrollableFrame(
|
||||||
POPUP, width=POPUP_SCROLL_WIDTH, height=POPUP_SCROLL_HEIGHT
|
popup, width=POPUP_SCROLL_WIDTH, height=POPUP_SCROLL_HEIGHT
|
||||||
)
|
)
|
||||||
scrollable_frame.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
|
scrollable_frame.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
|
||||||
|
|
||||||
def on_button_click(map, button_num):
|
def on_button_click(map, button_num):
|
||||||
map = update_popup_source(scrollable_frame, map, button_num)
|
map = self.update_popup_source(scrollable_frame, map, button_num)
|
||||||
|
|
||||||
for item in map:
|
for item in map:
|
||||||
id = item["id"]
|
id = item["id"]
|
||||||
|
@ -443,30 +467,31 @@ def create_source_target_popup(
|
||||||
target_image.grid(row=id, column=3, padx=10, pady=10)
|
target_image.grid(row=id, column=3, padx=10, pady=10)
|
||||||
target_image.configure(image=tk_image)
|
target_image.configure(image=tk_image)
|
||||||
|
|
||||||
popup_status_label = ctk.CTkLabel(POPUP, text=None, justify="center")
|
popup_status_label = ctk.CTkLabel(popup, text=None, justify="center")
|
||||||
popup_status_label.grid(row=1, column=0, pady=15)
|
popup_status_label.grid(row=1, column=0, pady=15)
|
||||||
|
|
||||||
close_button = ctk.CTkButton(
|
close_button = ctk.CTkButton(
|
||||||
POPUP, text="Submit", command=lambda: on_submit_click(start)
|
popup, text="Submit", command=lambda: on_submit_click(start)
|
||||||
)
|
)
|
||||||
close_button.grid(row=2, column=0, pady=10)
|
close_button.grid(row=2, column=0, pady=10)
|
||||||
|
|
||||||
|
self.popup_status_label = popup_status_label
|
||||||
|
self.popup = popup
|
||||||
|
|
||||||
def update_popup_source(
|
|
||||||
scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
|
|
||||||
) -> list:
|
|
||||||
global source_label_dict
|
|
||||||
|
|
||||||
|
def update_popup_source(
|
||||||
|
self, scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
|
||||||
|
) -> list:
|
||||||
source_path = ctk.filedialog.askopenfilename(
|
source_path = ctk.filedialog.askopenfilename(
|
||||||
title="select an source image",
|
title="select an source image",
|
||||||
initialdir=RECENT_DIRECTORY_SOURCE,
|
initialdir=self.recent_directory_source,
|
||||||
filetypes=[img_ft],
|
filetypes=[img_ft],
|
||||||
)
|
)
|
||||||
|
|
||||||
if "source" in map[button_num]:
|
if "source" in map[button_num]:
|
||||||
map[button_num].pop("source")
|
map[button_num].pop("source")
|
||||||
source_label_dict[button_num].destroy()
|
self.source_label_dict[button_num].destroy()
|
||||||
del source_label_dict[button_num]
|
del self.source_label_dict[button_num]
|
||||||
|
|
||||||
if source_path == "":
|
if source_path == "":
|
||||||
return map
|
return map
|
||||||
|
@ -498,78 +523,71 @@ def update_popup_source(
|
||||||
)
|
)
|
||||||
source_image.grid(row=button_num, column=1, padx=10, pady=10)
|
source_image.grid(row=button_num, column=1, padx=10, pady=10)
|
||||||
source_image.configure(image=tk_image)
|
source_image.configure(image=tk_image)
|
||||||
source_label_dict[button_num] = source_image
|
self.source_label_dict[button_num] = source_image
|
||||||
else:
|
else:
|
||||||
update_pop_status("Face could not be detected in last upload!")
|
self.update_pop_status("Face could not be detected in last upload!")
|
||||||
return map
|
return map
|
||||||
|
|
||||||
|
|
||||||
def create_preview(parent: ctk.CTkToplevel) -> ctk.CTkToplevel:
|
def create_preview(self, parent: ctk.CTkToplevel) -> ctk.CTkToplevel:
|
||||||
global preview_label, preview_slider
|
|
||||||
|
|
||||||
preview = ctk.CTkToplevel(parent)
|
preview = ctk.CTkToplevel(parent)
|
||||||
preview.withdraw()
|
preview.withdraw()
|
||||||
preview.title("Preview")
|
preview.title("Preview")
|
||||||
preview.configure()
|
preview.configure()
|
||||||
preview.protocol("WM_DELETE_WINDOW", lambda: toggle_preview())
|
preview.protocol("WM_DELETE_WINDOW", lambda: self.toggle_preview())
|
||||||
preview.resizable(width=True, height=True)
|
preview.resizable(width=True, height=True)
|
||||||
|
|
||||||
preview_label = ctk.CTkLabel(preview, text=None)
|
self.preview_label = ctk.CTkLabel(preview, text=None)
|
||||||
preview_label.pack(fill="both", expand=True)
|
self.preview_label.pack(fill="both", expand=True)
|
||||||
|
|
||||||
preview_slider = ctk.CTkSlider(
|
self.preview_slider = ctk.CTkSlider(
|
||||||
preview, from_=0, to=0, command=lambda frame_value: update_preview(frame_value)
|
preview, from_=0, to=0, command=lambda frame_value: self.update_preview(frame_value)
|
||||||
)
|
)
|
||||||
|
|
||||||
return preview
|
return preview
|
||||||
|
|
||||||
|
|
||||||
def update_status(text: str) -> None:
|
def update_status(self, text: str) -> None:
|
||||||
status_label.configure(text=text)
|
self.status_label.configure(text=text)
|
||||||
ROOT.update()
|
self.root.update()
|
||||||
|
|
||||||
|
|
||||||
def update_pop_status(text: str) -> None:
|
def update_pop_status(self, text: str) -> None:
|
||||||
popup_status_label.configure(text=text)
|
self.popup_status_label.configure(text=text)
|
||||||
|
|
||||||
|
|
||||||
def update_pop_live_status(text: str) -> None:
|
def update_pop_live_status(self, text: str) -> None:
|
||||||
popup_status_label_live.configure(text=text)
|
self.popup_status_label_live.configure(text=text)
|
||||||
|
|
||||||
|
|
||||||
def update_tumbler(var: str, value: bool) -> None:
|
def update_tumbler(self, var: str, value: bool) -> None:
|
||||||
modules.globals.fp_ui[var] = value
|
modules.globals.fp_ui[var] = value
|
||||||
save_switch_states()
|
save_switch_states()
|
||||||
# If we're currently in a live preview, update the frame processors
|
# If we're currently in a live preview, update the frame processors
|
||||||
if PREVIEW.state() == "normal":
|
if self.preview.state() == "normal":
|
||||||
global frame_processors
|
self.frame_processors = get_frame_processors_modules(
|
||||||
frame_processors = get_frame_processors_modules(
|
|
||||||
modules.globals.frame_processors
|
modules.globals.frame_processors
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def select_source_path() -> None:
|
def select_source_path(self) -> None:
|
||||||
global RECENT_DIRECTORY_SOURCE, img_ft, vid_ft
|
self.preview.withdraw()
|
||||||
|
|
||||||
PREVIEW.withdraw()
|
|
||||||
source_path = ctk.filedialog.askopenfilename(
|
source_path = ctk.filedialog.askopenfilename(
|
||||||
title="select an source image",
|
title="select an source image",
|
||||||
initialdir=RECENT_DIRECTORY_SOURCE,
|
initialdir=self.recent_directory_source,
|
||||||
filetypes=[img_ft],
|
filetypes=[img_ft],
|
||||||
)
|
)
|
||||||
if is_image(source_path):
|
if is_image(source_path):
|
||||||
modules.globals.source_path = source_path
|
modules.globals.source_path = source_path
|
||||||
RECENT_DIRECTORY_SOURCE = os.path.dirname(modules.globals.source_path)
|
self.recent_directory_source = os.path.dirname(modules.globals.source_path)
|
||||||
image = render_image_preview(modules.globals.source_path, (200, 200))
|
image = self.render_image_preview(modules.globals.source_path, (200, 200))
|
||||||
source_label.configure(image=image)
|
self.source_label.configure(image=image)
|
||||||
else:
|
else:
|
||||||
modules.globals.source_path = None
|
modules.globals.source_path = None
|
||||||
source_label.configure(image=None)
|
self.source_label.configure(image=None)
|
||||||
|
|
||||||
|
|
||||||
def swap_faces_paths() -> None:
|
def swap_faces_paths(self) -> None:
|
||||||
global RECENT_DIRECTORY_SOURCE, RECENT_DIRECTORY_TARGET
|
|
||||||
|
|
||||||
source_path = modules.globals.source_path
|
source_path = modules.globals.source_path
|
||||||
target_path = modules.globals.target_path
|
target_path = modules.globals.target_path
|
||||||
|
|
||||||
|
@ -579,52 +597,48 @@ def swap_faces_paths() -> None:
|
||||||
modules.globals.source_path = target_path
|
modules.globals.source_path = target_path
|
||||||
modules.globals.target_path = source_path
|
modules.globals.target_path = source_path
|
||||||
|
|
||||||
RECENT_DIRECTORY_SOURCE = os.path.dirname(modules.globals.source_path)
|
self.recent_directory_source = os.path.dirname(modules.globals.source_path)
|
||||||
RECENT_DIRECTORY_TARGET = os.path.dirname(modules.globals.target_path)
|
self.recent_directory_target = os.path.dirname(modules.globals.target_path)
|
||||||
|
|
||||||
PREVIEW.withdraw()
|
self.preview.withdraw()
|
||||||
|
|
||||||
source_image = render_image_preview(modules.globals.source_path, (200, 200))
|
source_image = self.render_image_preview(modules.globals.source_path, (200, 200))
|
||||||
source_label.configure(image=source_image)
|
self.source_label.configure(image=source_image)
|
||||||
|
|
||||||
target_image = render_image_preview(modules.globals.target_path, (200, 200))
|
target_image = self.render_image_preview(modules.globals.target_path, (200, 200))
|
||||||
target_label.configure(image=target_image)
|
self.target_label.configure(image=target_image)
|
||||||
|
|
||||||
|
|
||||||
def select_target_path() -> None:
|
def select_target_path(self) -> None:
|
||||||
global RECENT_DIRECTORY_TARGET, img_ft, vid_ft
|
self.preview.withdraw()
|
||||||
|
|
||||||
PREVIEW.withdraw()
|
|
||||||
target_path = ctk.filedialog.askopenfilename(
|
target_path = ctk.filedialog.askopenfilename(
|
||||||
title="select an target image or video",
|
title="select an target image or video",
|
||||||
initialdir=RECENT_DIRECTORY_TARGET,
|
initialdir=self.recent_directory_target,
|
||||||
filetypes=[img_ft, vid_ft],
|
filetypes=[img_ft, vid_ft],
|
||||||
)
|
)
|
||||||
if is_image(target_path):
|
if is_image(target_path):
|
||||||
modules.globals.target_path = target_path
|
modules.globals.target_path = target_path
|
||||||
RECENT_DIRECTORY_TARGET = os.path.dirname(modules.globals.target_path)
|
self.recent_directory_target = os.path.dirname(modules.globals.target_path)
|
||||||
image = render_image_preview(modules.globals.target_path, (200, 200))
|
image = self.render_image_preview(modules.globals.target_path, (200, 200))
|
||||||
target_label.configure(image=image)
|
self.target_label.configure(image=image)
|
||||||
elif is_video(target_path):
|
elif is_video(target_path):
|
||||||
modules.globals.target_path = target_path
|
modules.globals.target_path = target_path
|
||||||
RECENT_DIRECTORY_TARGET = os.path.dirname(modules.globals.target_path)
|
self.recent_directory_target = os.path.dirname(modules.globals.target_path)
|
||||||
video_frame = render_video_preview(target_path, (200, 200))
|
video_frame = self.render_video_preview(target_path, (200, 200))
|
||||||
target_label.configure(image=video_frame)
|
self.target_label.configure(image=video_frame)
|
||||||
else:
|
else:
|
||||||
modules.globals.target_path = None
|
modules.globals.target_path = None
|
||||||
target_label.configure(image=None)
|
self.target_label.configure(image=None)
|
||||||
|
|
||||||
|
|
||||||
def select_output_path(start: Callable[[], None]) -> None:
|
def select_output_path(self, start: Callable[[], None]) -> None:
|
||||||
global RECENT_DIRECTORY_OUTPUT, img_ft, vid_ft
|
|
||||||
|
|
||||||
if is_image(modules.globals.target_path):
|
if is_image(modules.globals.target_path):
|
||||||
output_path = ctk.filedialog.asksaveasfilename(
|
output_path = ctk.filedialog.asksaveasfilename(
|
||||||
title="save image output file",
|
title="save image output file",
|
||||||
filetypes=[img_ft],
|
filetypes=[img_ft],
|
||||||
defaultextension=".png",
|
defaultextension=".png",
|
||||||
initialfile="output.png",
|
initialfile="output.png",
|
||||||
initialdir=RECENT_DIRECTORY_OUTPUT,
|
initialdir=self.recent_directory_output,
|
||||||
)
|
)
|
||||||
elif is_video(modules.globals.target_path):
|
elif is_video(modules.globals.target_path):
|
||||||
output_path = ctk.filedialog.asksaveasfilename(
|
output_path = ctk.filedialog.asksaveasfilename(
|
||||||
|
@ -632,22 +646,19 @@ def select_output_path(start: Callable[[], None]) -> None:
|
||||||
filetypes=[vid_ft],
|
filetypes=[vid_ft],
|
||||||
defaultextension=".mp4",
|
defaultextension=".mp4",
|
||||||
initialfile="output.mp4",
|
initialfile="output.mp4",
|
||||||
initialdir=RECENT_DIRECTORY_OUTPUT,
|
initialdir=self.recent_directory_output,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
output_path = None
|
output_path = None
|
||||||
if output_path:
|
if output_path:
|
||||||
modules.globals.output_path = output_path
|
modules.globals.output_path = output_path
|
||||||
RECENT_DIRECTORY_OUTPUT = os.path.dirname(modules.globals.output_path)
|
self.recent_directory_output = os.path.dirname(modules.globals.output_path)
|
||||||
start()
|
start()
|
||||||
|
|
||||||
|
def check_and_ignore_nsfw(self, target: str | ndarray, destroy: Callable | None = None) -> bool:
|
||||||
def check_and_ignore_nsfw(target, destroy: Callable = None) -> bool:
|
|
||||||
"""Check if the target is NSFW.
|
"""Check if the target is NSFW.
|
||||||
TODO: Consider to make blur the target.
|
TODO: Consider to make blur the target.
|
||||||
"""
|
"""
|
||||||
from numpy import ndarray
|
|
||||||
from modules.predicter import predict_image, predict_video, predict_frame
|
|
||||||
|
|
||||||
if type(target) is str: # image/video file path
|
if type(target) is str: # image/video file path
|
||||||
check_nsfw = predict_image if has_image_extension(target) else predict_video
|
check_nsfw = predict_image if has_image_extension(target) else predict_video
|
||||||
|
@ -658,37 +669,21 @@ def check_and_ignore_nsfw(target, destroy: Callable = None) -> bool:
|
||||||
destroy(
|
destroy(
|
||||||
to_quit=False
|
to_quit=False
|
||||||
) # Do not need to destroy the window frame if the target is NSFW
|
) # Do not need to destroy the window frame if the target is NSFW
|
||||||
update_status("Processing ignored!")
|
self.update_status("Processing ignored!")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def render_image_preview(self, image_path: str, size: Tuple[int, int]) -> ctk.CTkImage:
|
||||||
def fit_image_to_size(image, width: int, height: int):
|
|
||||||
if width is None and height is None:
|
|
||||||
return image
|
|
||||||
h, w, _ = image.shape
|
|
||||||
ratio_h = 0.0
|
|
||||||
ratio_w = 0.0
|
|
||||||
if width > height:
|
|
||||||
ratio_h = height / h
|
|
||||||
else:
|
|
||||||
ratio_w = width / w
|
|
||||||
ratio = max(ratio_w, ratio_h)
|
|
||||||
new_size = (int(ratio * w), int(ratio * h))
|
|
||||||
return cv2.resize(image, dsize=new_size)
|
|
||||||
|
|
||||||
|
|
||||||
def render_image_preview(image_path: str, size: Tuple[int, int]) -> ctk.CTkImage:
|
|
||||||
image = Image.open(image_path)
|
image = Image.open(image_path)
|
||||||
if size:
|
if size:
|
||||||
image = ImageOps.fit(image, size, Image.LANCZOS)
|
image = ImageOps.fit(image, size, Image.LANCZOS)
|
||||||
return ctk.CTkImage(image, size=image.size)
|
return ctk.CTkImage(image, size=image.size)
|
||||||
|
|
||||||
|
|
||||||
def render_video_preview(
|
def render_video_preview(
|
||||||
video_path: str, size: Tuple[int, int], frame_number: int = 0
|
self, video_path: str, size: Tuple[int, int], frame_number: int = 0
|
||||||
) -> ctk.CTkImage:
|
) -> None:
|
||||||
capture = cv2.VideoCapture(video_path)
|
capture = cv2.VideoCapture(video_path)
|
||||||
if frame_number:
|
if frame_number:
|
||||||
capture.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
|
capture.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
|
||||||
|
@ -702,29 +697,29 @@ def render_video_preview(
|
||||||
cv2.destroyAllWindows()
|
cv2.destroyAllWindows()
|
||||||
|
|
||||||
|
|
||||||
def toggle_preview() -> None:
|
def toggle_preview(self) -> None:
|
||||||
if PREVIEW.state() == "normal":
|
if self.preview.state() == "normal":
|
||||||
PREVIEW.withdraw()
|
self.preview.withdraw()
|
||||||
elif modules.globals.source_path and modules.globals.target_path:
|
elif modules.globals.source_path and modules.globals.target_path:
|
||||||
init_preview()
|
self.init_preview()
|
||||||
update_preview()
|
self.update_preview()
|
||||||
|
|
||||||
|
|
||||||
def init_preview() -> None:
|
def init_preview(self) -> None:
|
||||||
if is_image(modules.globals.target_path):
|
if is_image(modules.globals.target_path):
|
||||||
preview_slider.pack_forget()
|
self.preview_slider.pack_forget()
|
||||||
if is_video(modules.globals.target_path):
|
if is_video(modules.globals.target_path):
|
||||||
video_frame_total = get_video_frame_total(modules.globals.target_path)
|
video_frame_total = get_video_frame_total(modules.globals.target_path)
|
||||||
preview_slider.configure(to=video_frame_total)
|
self.preview_slider.configure(to=video_frame_total)
|
||||||
preview_slider.pack(fill="x")
|
self.preview_slider.pack(fill="x")
|
||||||
preview_slider.set(0)
|
self.preview_slider.set(0)
|
||||||
|
|
||||||
|
|
||||||
def update_preview(frame_number: int = 0) -> None:
|
def update_preview(self, frame_number: int = 0) -> None:
|
||||||
if modules.globals.source_path and modules.globals.target_path:
|
if modules.globals.source_path and modules.globals.target_path:
|
||||||
update_status("Processing...")
|
self.update_status("Processing...")
|
||||||
temp_frame = get_video_frame(modules.globals.target_path, frame_number)
|
temp_frame = get_video_frame(modules.globals.target_path, frame_number)
|
||||||
if modules.globals.nsfw_filter and check_and_ignore_nsfw(temp_frame):
|
if modules.globals.nsfw_filter and self.check_and_ignore_nsfw(temp_frame):
|
||||||
return
|
return
|
||||||
for frame_processor in get_frame_processors_modules(
|
for frame_processor in get_frame_processors_modules(
|
||||||
modules.globals.frame_processors
|
modules.globals.frame_processors
|
||||||
|
@ -737,49 +732,32 @@ def update_preview(frame_number: int = 0) -> None:
|
||||||
image, (PREVIEW_MAX_WIDTH, PREVIEW_MAX_HEIGHT), Image.LANCZOS
|
image, (PREVIEW_MAX_WIDTH, PREVIEW_MAX_HEIGHT), Image.LANCZOS
|
||||||
)
|
)
|
||||||
image = ctk.CTkImage(image, size=image.size)
|
image = ctk.CTkImage(image, size=image.size)
|
||||||
preview_label.configure(image=image)
|
self.preview_label.configure(image=image)
|
||||||
update_status("Processing succeed!")
|
self.update_status("Processing succeed!")
|
||||||
PREVIEW.deiconify()
|
self.preview.deiconify()
|
||||||
|
|
||||||
|
|
||||||
def webcam_preview(root: ctk.CTk, camera_index: int):
|
def webcam_preview(self, root: ctk.CTk, camera_index: int):
|
||||||
if not modules.globals.map_faces:
|
if not modules.globals.map_faces:
|
||||||
if modules.globals.source_path is None:
|
if modules.globals.source_path is None:
|
||||||
# No image selected
|
# No image selected
|
||||||
return
|
return
|
||||||
create_webcam_preview(camera_index)
|
self.create_webcam_preview(camera_index)
|
||||||
else:
|
else:
|
||||||
modules.globals.souce_target_map = []
|
modules.globals.souce_target_map = []
|
||||||
create_source_target_popup_for_webcam(
|
self.create_source_target_popup_for_webcam(
|
||||||
root, modules.globals.souce_target_map, camera_index
|
root, modules.globals.souce_target_map, camera_index
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def create_webcam_preview(self, camera_index: int):
|
||||||
def get_available_cameras():
|
|
||||||
"""Returns a list of available camera names and indices."""
|
|
||||||
camera_indices = []
|
|
||||||
camera_names = []
|
|
||||||
|
|
||||||
for camera in enumerate_cameras():
|
|
||||||
cap = cv2.VideoCapture(camera.index)
|
|
||||||
if cap.isOpened():
|
|
||||||
camera_indices.append(camera.index)
|
|
||||||
camera_names.append(camera.name)
|
|
||||||
cap.release()
|
|
||||||
return (camera_indices, camera_names)
|
|
||||||
|
|
||||||
|
|
||||||
def create_webcam_preview(camera_index: int):
|
|
||||||
global preview_label, PREVIEW
|
|
||||||
|
|
||||||
camera = cv2.VideoCapture(camera_index)
|
camera = cv2.VideoCapture(camera_index)
|
||||||
camera.set(cv2.CAP_PROP_FRAME_WIDTH, PREVIEW_DEFAULT_WIDTH)
|
camera.set(cv2.CAP_PROP_FRAME_WIDTH, PREVIEW_DEFAULT_WIDTH)
|
||||||
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, PREVIEW_DEFAULT_HEIGHT)
|
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, PREVIEW_DEFAULT_HEIGHT)
|
||||||
camera.set(cv2.CAP_PROP_FPS, 60)
|
camera.set(cv2.CAP_PROP_FPS, 60)
|
||||||
|
|
||||||
preview_label.configure(width=PREVIEW_DEFAULT_WIDTH, height=PREVIEW_DEFAULT_HEIGHT)
|
self.preview_label.configure(width=PREVIEW_DEFAULT_WIDTH, height=PREVIEW_DEFAULT_HEIGHT)
|
||||||
|
|
||||||
PREVIEW.deiconify()
|
self.preview.deiconify()
|
||||||
|
|
||||||
frame_processors = get_frame_processors_modules(modules.globals.frame_processors)
|
frame_processors = get_frame_processors_modules(modules.globals.frame_processors)
|
||||||
|
|
||||||
|
@ -801,7 +779,7 @@ def create_webcam_preview(camera_index: int):
|
||||||
|
|
||||||
if modules.globals.live_resizable:
|
if modules.globals.live_resizable:
|
||||||
temp_frame = fit_image_to_size(
|
temp_frame = fit_image_to_size(
|
||||||
temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height()
|
temp_frame, self.preview.winfo_width(), self.preview.winfo_height()
|
||||||
)
|
)
|
||||||
|
|
||||||
if not modules.globals.map_faces:
|
if not modules.globals.map_faces:
|
||||||
|
@ -849,64 +827,62 @@ def create_webcam_preview(camera_index: int):
|
||||||
image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS
|
image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS
|
||||||
)
|
)
|
||||||
image = ctk.CTkImage(image, size=image.size)
|
image = ctk.CTkImage(image, size=image.size)
|
||||||
preview_label.configure(image=image)
|
self.preview_label.configure(image=image)
|
||||||
ROOT.update()
|
self.root.update()
|
||||||
|
|
||||||
if PREVIEW.state() == "withdrawn":
|
if self.preview.state() == "withdrawn":
|
||||||
break
|
break
|
||||||
|
|
||||||
camera.release()
|
camera.release()
|
||||||
PREVIEW.withdraw()
|
self.preview.withdraw()
|
||||||
|
|
||||||
|
|
||||||
def create_source_target_popup_for_webcam(
|
def create_source_target_popup_for_webcam(
|
||||||
root: ctk.CTk, map: list, camera_index: int
|
self, root: ctk.CTk, map: list, camera_index: int
|
||||||
) -> None:
|
) -> None:
|
||||||
global POPUP_LIVE, popup_status_label_live
|
self.popup_live = ctk.CTkToplevel(root)
|
||||||
|
self.popup_live.title("Source x Target Mapper")
|
||||||
POPUP_LIVE = ctk.CTkToplevel(root)
|
self.popup_live.geometry(f"{POPUP_LIVE_WIDTH}x{POPUP_LIVE_HEIGHT}")
|
||||||
POPUP_LIVE.title("Source x Target Mapper")
|
self.popup_live.focus()
|
||||||
POPUP_LIVE.geometry(f"{POPUP_LIVE_WIDTH}x{POPUP_LIVE_HEIGHT}")
|
|
||||||
POPUP_LIVE.focus()
|
|
||||||
|
|
||||||
def on_submit_click():
|
def on_submit_click():
|
||||||
if has_valid_map():
|
if has_valid_map():
|
||||||
POPUP_LIVE.destroy()
|
self.popup_live.destroy()
|
||||||
simplify_maps()
|
simplify_maps()
|
||||||
create_webcam_preview(camera_index)
|
self.create_webcam_preview(camera_index)
|
||||||
else:
|
else:
|
||||||
update_pop_live_status("At least 1 source with target is required!")
|
self.update_pop_live_status("At least 1 source with target is required!")
|
||||||
|
|
||||||
def on_add_click():
|
def on_add_click():
|
||||||
add_blank_map()
|
add_blank_map()
|
||||||
refresh_data(map)
|
self.refresh_data(map)
|
||||||
update_pop_live_status("Please provide mapping!")
|
self.update_pop_live_status("Please provide mapping!")
|
||||||
|
|
||||||
popup_status_label_live = ctk.CTkLabel(POPUP_LIVE, text=None, justify="center")
|
popup_status_label_live = ctk.CTkLabel(self.popup_live, text=None, justify="center")
|
||||||
popup_status_label_live.grid(row=1, column=0, pady=15)
|
popup_status_label_live.grid(row=1, column=0, pady=15)
|
||||||
|
|
||||||
add_button = ctk.CTkButton(POPUP_LIVE, text="Add", command=lambda: on_add_click())
|
add_button = ctk.CTkButton(self.popup_live, text="Add", command=lambda: on_add_click())
|
||||||
add_button.place(relx=0.2, rely=0.92, relwidth=0.2, relheight=0.05)
|
add_button.place(relx=0.2, rely=0.92, relwidth=0.2, relheight=0.05)
|
||||||
|
|
||||||
close_button = ctk.CTkButton(
|
close_button = ctk.CTkButton(
|
||||||
POPUP_LIVE, text="Submit", command=lambda: on_submit_click()
|
self.popup_live, text="Submit", command=lambda: on_submit_click()
|
||||||
)
|
)
|
||||||
close_button.place(relx=0.6, rely=0.92, relwidth=0.2, relheight=0.05)
|
close_button.place(relx=0.6, rely=0.92, relwidth=0.2, relheight=0.05)
|
||||||
|
|
||||||
|
self.popup_status_label_live = popup_status_label_live
|
||||||
|
|
||||||
def refresh_data(map: list):
|
|
||||||
global POPUP_LIVE
|
|
||||||
|
|
||||||
|
def refresh_data(self, map: list):
|
||||||
scrollable_frame = ctk.CTkScrollableFrame(
|
scrollable_frame = ctk.CTkScrollableFrame(
|
||||||
POPUP_LIVE, width=POPUP_LIVE_SCROLL_WIDTH, height=POPUP_LIVE_SCROLL_HEIGHT
|
self.popup_live, width=POPUP_LIVE_SCROLL_WIDTH, height=POPUP_LIVE_SCROLL_HEIGHT
|
||||||
)
|
)
|
||||||
scrollable_frame.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
|
scrollable_frame.grid(row=0, column=0, padx=0, pady=0, sticky="nsew")
|
||||||
|
|
||||||
def on_sbutton_click(map, button_num):
|
def on_sbutton_click(map, button_num):
|
||||||
map = update_webcam_source(scrollable_frame, map, button_num)
|
map = self.update_webcam_source(scrollable_frame, map, button_num)
|
||||||
|
|
||||||
def on_tbutton_click(map, button_num):
|
def on_tbutton_click(map, button_num):
|
||||||
map = update_webcam_target(scrollable_frame, map, button_num)
|
map = self.update_webcam_target(scrollable_frame, map, button_num)
|
||||||
|
|
||||||
for item in map:
|
for item in map:
|
||||||
id = item["id"]
|
id = item["id"]
|
||||||
|
@ -974,21 +950,19 @@ def refresh_data(map: list):
|
||||||
target_image.configure(image=tk_image)
|
target_image.configure(image=tk_image)
|
||||||
|
|
||||||
|
|
||||||
def update_webcam_source(
|
def update_webcam_source(
|
||||||
scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
|
self, scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
|
||||||
) -> list:
|
) -> list:
|
||||||
global source_label_dict_live
|
|
||||||
|
|
||||||
source_path = ctk.filedialog.askopenfilename(
|
source_path = ctk.filedialog.askopenfilename(
|
||||||
title="select an source image",
|
title="select an source image",
|
||||||
initialdir=RECENT_DIRECTORY_SOURCE,
|
initialdir=self.recent_directory_source,
|
||||||
filetypes=[img_ft],
|
filetypes=[img_ft],
|
||||||
)
|
)
|
||||||
|
|
||||||
if "source" in map[button_num]:
|
if "source" in map[button_num]:
|
||||||
map[button_num].pop("source")
|
map[button_num].pop("source")
|
||||||
source_label_dict_live[button_num].destroy()
|
self.source_label_dict_live[button_num].destroy()
|
||||||
del source_label_dict_live[button_num]
|
del self.source_label_dict_live[button_num]
|
||||||
|
|
||||||
if source_path == "":
|
if source_path == "":
|
||||||
return map
|
return map
|
||||||
|
@ -1020,27 +994,25 @@ def update_webcam_source(
|
||||||
)
|
)
|
||||||
source_image.grid(row=button_num, column=1, padx=10, pady=10)
|
source_image.grid(row=button_num, column=1, padx=10, pady=10)
|
||||||
source_image.configure(image=tk_image)
|
source_image.configure(image=tk_image)
|
||||||
source_label_dict_live[button_num] = source_image
|
self.source_label_dict_live[button_num] = source_image
|
||||||
else:
|
else:
|
||||||
update_pop_live_status("Face could not be detected in last upload!")
|
self.update_pop_live_status("Face could not be detected in last upload!")
|
||||||
return map
|
return map
|
||||||
|
|
||||||
|
|
||||||
def update_webcam_target(
|
def update_webcam_target(
|
||||||
scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
|
self, scrollable_frame: ctk.CTkScrollableFrame, map: list, button_num: int
|
||||||
) -> list:
|
) -> list:
|
||||||
global target_label_dict_live
|
|
||||||
|
|
||||||
target_path = ctk.filedialog.askopenfilename(
|
target_path = ctk.filedialog.askopenfilename(
|
||||||
title="select an target image",
|
title="select an target image",
|
||||||
initialdir=RECENT_DIRECTORY_SOURCE,
|
initialdir=self.recent_directory_source,
|
||||||
filetypes=[img_ft],
|
filetypes=[img_ft],
|
||||||
)
|
)
|
||||||
|
|
||||||
if "target" in map[button_num]:
|
if "target" in map[button_num]:
|
||||||
map[button_num].pop("target")
|
map[button_num].pop("target")
|
||||||
target_label_dict_live[button_num].destroy()
|
self.target_label_dict_live[button_num].destroy()
|
||||||
del target_label_dict_live[button_num]
|
del self.target_label_dict_live[button_num]
|
||||||
|
|
||||||
if target_path == "":
|
if target_path == "":
|
||||||
return map
|
return map
|
||||||
|
@ -1072,7 +1044,7 @@ def update_webcam_target(
|
||||||
)
|
)
|
||||||
target_image.grid(row=button_num, column=4, padx=20, pady=10)
|
target_image.grid(row=button_num, column=4, padx=20, pady=10)
|
||||||
target_image.configure(image=tk_image)
|
target_image.configure(image=tk_image)
|
||||||
target_label_dict_live[button_num] = target_image
|
self.target_label_dict_live[button_num] = target_image
|
||||||
else:
|
else:
|
||||||
update_pop_live_status("Face could not be detected in last upload!")
|
self.update_pop_live_status("Face could not be detected in last upload!")
|
||||||
return map
|
return map
|
||||||
|
|
Loading…
Reference in New Issue