@ -4,13 +4,26 @@ import customtkinter as ctk
from typing import Callable , Tuple
import cv2
from PIL import Image , ImageOps
import tkinterdnd2 as tkdnd
import modules . globals
import modules . metadata
from modules . face_analyser import get_one_face , get_unique_faces_from_target_image , get_unique_faces_from_target_video , add_blank_map , has_valid_map , simplify_maps
from modules . face_analyser import (
get_one_face ,
get_unique_faces_from_target_image ,
get_unique_faces_from_target_video ,
add_blank_map ,
has_valid_map ,
simplify_maps ,
)
from modules . capturer import get_video_frame , get_video_frame_total
from modules . processors . frame . core import get_frame_processors_modules
from modules . utilities import is_image , is_video , resolve_relative_path , has_image_extension
from modules . utilities import (
is_image ,
is_video ,
resolve_relative_path ,
has_image_extension ,
)
ROOT = None
POPUP = None
@ -26,12 +39,12 @@ PREVIEW_DEFAULT_HEIGHT = 540
POPUP_WIDTH = 750
POPUP_HEIGHT = 810
POPUP_SCROLL_WIDTH = 740 ,
POPUP_SCROLL_WIDTH = 740
POPUP_SCROLL_HEIGHT = 700
POPUP_LIVE_WIDTH = 900
POPUP_LIVE_HEIGHT = 820
POPUP_LIVE_SCROLL_WIDTH = 890 ,
POPUP_LIVE_SCROLL_WIDTH = 890
POPUP_LIVE_SCROLL_HEIGHT = 700
MAPPER_PREVIEW_MAX_HEIGHT = 100
@ -58,7 +71,58 @@ target_label_dict_live = {}
img_ft , vid_ft = modules . globals . file_types
def init ( start : Callable [ [ ] , None ] , destroy : Callable [ [ ] , None ] ) - > ctk . CTk :
class DragDropButton ( ctk . CTkButton ) :
def __init__ ( self , master , * * kwargs ) :
super ( ) . __init__ ( master , * * kwargs )
self . drop_target_register ( tkdnd . DND_FILES )
self . dnd_bind ( " <<Drop>> " , self . drop )
def drop ( self , event ) :
file_path = event . data
if file_path . startswith ( " { " ) :
file_path = file_path [ 1 : - 1 ]
self . handle_drop ( file_path )
def handle_drop ( self , file_path ) :
pass
class SourceButton ( DragDropButton ) :
def handle_drop ( self , file_path ) :
if is_image ( file_path ) :
modules . globals . source_path = file_path
global RECENT_DIRECTORY_SOURCE
RECENT_DIRECTORY_SOURCE = os . path . dirname ( modules . globals . source_path )
image = render_image_preview ( modules . globals . source_path , ( 200 , 200 ) )
source_label . configure ( image = image )
class SourceMapperButton ( DragDropButton ) :
def __init__ ( self , master , map , button_num , * * kwargs ) :
super ( ) . __init__ ( master , * * kwargs )
self . map = map
self . button_num = button_num
def handle_drop ( self , file_path ) :
if is_image ( file_path ) :
update_popup_source ( self . master , self . map , self . button_num , file_path )
class TargetButton ( DragDropButton ) :
def handle_drop ( self , file_path ) :
global RECENT_DIRECTORY_TARGET
if is_image ( file_path ) or is_video ( file_path ) :
modules . globals . target_path = file_path
RECENT_DIRECTORY_TARGET = os . path . dirname ( modules . globals . target_path )
if is_image ( file_path ) :
image = render_image_preview ( modules . globals . target_path , ( 200 , 200 ) )
target_label . configure ( image = image )
elif is_video ( file_path ) :
video_frame = render_video_preview ( file_path , ( 200 , 200 ) )
target_label . configure ( image = video_frame )
def init ( start : Callable [ [ ] , None ] , destroy : Callable [ [ ] , None ] ) - > tkdnd . TkinterDnD . Tk :
global ROOT , PREVIEW
ROOT = create_root ( start , destroy )
@ -67,18 +131,22 @@ def init(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk:
return ROOT
def create_root ( start : Callable [ [ ] , None ] , destroy : Callable [ [ ] , None ] ) - > ctk . CTk :
def create_root (
start : Callable [ [ ] , None ] , destroy : Callable [ [ ] , None ]
) - > tkdnd . TkinterDnD . Tk :
global source_label , target_label , status_label
ctk . deactivate_automatic_dpi_awareness ( )
ctk . set_appearance_mode ( ' system ' )
ctk . set_default_color_theme ( resolve_relative_path ( ' ui.json ' ) )
ctk . set_appearance_mode ( " dark " ) # Re-add this line
ctk . set_default_color_theme ( resolve_relative_path ( " ui.json " ) )
root = ctk. C Tk( )
root = tkdnd. TkinterDnD . Tk( )
root . minsize ( ROOT_WIDTH , ROOT_HEIGHT )
root . title ( f ' { modules . metadata . name } { modules . metadata . version } { modules . metadata . edition } ' )
root . configure ( )
root . protocol ( ' WM_DELETE_WINDOW ' , lambda : destroy ( ) )
root . title (
f " { modules . metadata . name } { modules . metadata . version } { modules . metadata . edition } "
)
root . configure ( bg = " gray12 " )
root . protocol ( " WM_DELETE_WINDOW " , lambda : destroy ( ) )
source_label = ctk . CTkLabel ( root , text = None )
source_label . place ( relx = 0.1 , rely = 0.1 , relwidth = 0.3 , relheight = 0.25 )
@ -86,71 +154,137 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
target_label = ctk . CTkLabel ( root , text = None )
target_label . place ( relx = 0.6 , rely = 0.1 , relwidth = 0.3 , relheight = 0.25 )
select_face_button = ctk . CTkButton ( root , text = ' Select a face ' , cursor = ' hand2 ' , command = lambda : select_source_path ( ) )
select_face_button = SourceButton (
root , text = " Select a face " , cursor = " hand2 " , command = lambda : select_source_path ( )
)
select_face_button . place ( relx = 0.1 , rely = 0.4 , relwidth = 0.3 , relheight = 0.1 )
swap_faces_button = ctk . CTkButton ( root , text = ' ↔ ' , cursor = ' hand2 ' , command = lambda : swap_faces_paths ( ) )
swap_faces_button = ctk . CTkButton (
root , text = " ↔ " , cursor = " hand2 " , command = lambda : swap_faces_paths ( )
)
swap_faces_button . place ( relx = 0.45 , rely = 0.4 , relwidth = 0.1 , relheight = 0.1 )
select_target_button = ctk . CTkButton ( root , text = ' Select a target ' , cursor = ' hand2 ' , command = lambda : select_target_path ( ) )
select_target_button = TargetButton (
root ,
text = " Select a target " ,
cursor = " hand2 " ,
command = lambda : select_target_path ( ) ,
)
select_target_button . place ( relx = 0.6 , rely = 0.4 , relwidth = 0.3 , relheight = 0.1 )
keep_fps_value = ctk . BooleanVar ( value = modules . globals . keep_fps )
keep_fps_checkbox = ctk . CTkSwitch ( root , text = ' Keep fps ' , variable = keep_fps_value , cursor = ' hand2 ' , command = lambda : setattr ( modules . globals , ' keep_fps ' , not modules . globals . keep_fps ) )
keep_fps_checkbox = ctk . CTkSwitch (
root ,
text = " Keep fps " ,
variable = keep_fps_value ,
cursor = " hand2 " ,
command = lambda : setattr (
modules . globals , " keep_fps " , not modules . globals . keep_fps
) ,
)
keep_fps_checkbox . place ( relx = 0.1 , rely = 0.6 )
keep_frames_value = ctk . BooleanVar ( value = modules . globals . keep_frames )
keep_frames_switch = ctk . CTkSwitch ( root , text = ' Keep frames ' , variable = keep_frames_value , cursor = ' hand2 ' , command = lambda : setattr ( modules . globals , ' keep_frames ' , keep_frames_value . get ( ) ) )
keep_frames_switch = ctk . CTkSwitch (
root ,
text = " Keep frames " ,
variable = keep_frames_value ,
cursor = " hand2 " ,
command = lambda : setattr (
modules . globals , " keep_frames " , keep_frames_value . get ( )
) ,
)
keep_frames_switch . place ( relx = 0.1 , rely = 0.65 )
# for FRAME PROCESSOR ENHANCER tumbler:
enhancer_value = ctk . BooleanVar ( value = modules . globals . fp_ui [ ' face_enhancer ' ] )
enhancer_switch = ctk . CTkSwitch ( root , text = ' Face Enhancer ' , variable = enhancer_value , cursor = ' hand2 ' , command = lambda : update_tumbler ( ' face_enhancer ' , enhancer_value . get ( ) ) )
enhancer_value = ctk . BooleanVar ( value = modules . globals . fp_ui [ " face_enhancer " ] )
enhancer_switch = ctk . CTkSwitch (
root ,
text = " Face Enhancer " ,
variable = enhancer_value ,
cursor = " hand2 " ,
command = lambda : update_tumbler ( " face_enhancer " , enhancer_value . get ( ) ) ,
)
enhancer_switch . place ( relx = 0.1 , rely = 0.7 )
keep_audio_value = ctk . BooleanVar ( value = modules . globals . keep_audio )
keep_audio_switch = ctk . CTkSwitch ( root , text = ' Keep audio ' , variable = keep_audio_value , cursor = ' hand2 ' , command = lambda : setattr ( modules . globals , ' keep_audio ' , keep_audio_value . get ( ) ) )
keep_audio_switch = ctk . CTkSwitch (
root ,
text = " Keep audio " ,
variable = keep_audio_value ,
cursor = " hand2 " ,
command = lambda : setattr ( modules . globals , " keep_audio " , keep_audio_value . get ( ) ) ,
)
keep_audio_switch . place ( relx = 0.6 , rely = 0.6 )
many_faces_value = ctk . BooleanVar ( value = modules . globals . many_faces )
many_faces_switch = ctk . CTkSwitch ( root , text = ' Many faces ' , variable = many_faces_value , cursor = ' hand2 ' , command = lambda : setattr ( modules . globals , ' many_faces ' , many_faces_value . get ( ) ) )
many_faces_switch = ctk . CTkSwitch (
root ,
text = " Many faces " ,
variable = many_faces_value ,
cursor = " hand2 " ,
command = lambda : setattr ( modules . globals , " many_faces " , many_faces_value . get ( ) ) ,
)
many_faces_switch . place ( relx = 0.6 , rely = 0.65 )
# Add color correction toggle button
color_correction_value = ctk . BooleanVar ( value = modules . globals . color_correction )
color_correction_switch = ctk . CTkSwitch ( root , text = ' Fix Blueish Cam \n (force cv2 to use RGB instead of BGR) ' , variable = color_correction_value , cursor = ' hand2 ' , command = lambda : setattr ( modules . globals , ' color_correction ' , color_correction_value . get ( ) ) )
color_correction_switch = ctk . CTkSwitch (
root ,
text = " Fix Blueish Cam \n (force cv2 to use RGB instead of BGR) " ,
variable = color_correction_value ,
cursor = " hand2 " ,
command = lambda : setattr (
modules . globals , " color_correction " , color_correction_value . get ( )
) ,
)
color_correction_switch . place ( relx = 0.6 , rely = 0.70 )
# nsfw_value = ctk.BooleanVar(value=modules.globals.nsfw_filter)
# nsfw_switch = ctk.CTkSwitch(root, text='NSFW filter', variable=nsfw_value, cursor='hand2', command=lambda: setattr(modules.globals, 'nsfw_filter', nsfw_value.get()))
# nsfw_switch.place(relx=0.6, rely=0.7)
map_faces = ctk . BooleanVar ( value = modules . globals . map_faces )
map_faces_switch = ctk . CTkSwitch ( root , text = ' Map faces ' , variable = map_faces , cursor = ' hand2 ' , command = lambda : setattr ( modules . globals , ' map_faces ' , map_faces . get ( ) ) )
map_faces_switch = ctk . CTkSwitch (
root ,
text = " Map faces " ,
variable = map_faces ,
cursor = " hand2 " ,
command = lambda : setattr ( modules . globals , " map_faces " , map_faces . get ( ) ) ,
)
map_faces_switch . place ( relx = 0.1 , rely = 0.75 )
start_button = ctk . CTkButton ( root , text = ' Start ' , cursor = ' hand2 ' , command = lambda : analyze_target ( start , root ) )
start_button = ctk . CTkButton (
root , text = " Start " , cursor = " hand2 " , command = lambda : analyze_target ( start , root )
)
start_button . place ( relx = 0.15 , rely = 0.80 , relwidth = 0.2 , relheight = 0.05 )
stop_button = ctk . CTkButton ( root , text = ' Destroy ' , cursor = ' hand2 ' , command = lambda : destroy ( ) )
stop_button = ctk . CTkButton (
root , text = " Destroy " , cursor = " hand2 " , command = lambda : destroy ( )
)
stop_button . place ( relx = 0.4 , rely = 0.80 , relwidth = 0.2 , relheight = 0.05 )
preview_button = ctk . CTkButton ( root , text = ' Preview ' , cursor = ' hand2 ' , command = lambda : toggle_preview ( ) )
preview_button = ctk . CTkButton (
root , text = " Preview " , cursor = " hand2 " , command = lambda : toggle_preview ( )
)
preview_button . place ( relx = 0.65 , rely = 0.80 , relwidth = 0.2 , relheight = 0.05 )
live_button = ctk . CTkButton ( root , text = ' Live ' , cursor = ' hand2 ' , command = lambda : webcam_preview ( root ) )
live_button = ctk . CTkButton (
root , text = " Live " , cursor = " hand2 " , command = lambda : webcam_preview ( root )
)
live_button . place ( relx = 0.40 , rely = 0.86 , relwidth = 0.2 , relheight = 0.05 )
status_label = ctk . CTkLabel ( root , text = None , justify = ' center ' )
status_label = ctk . CTkLabel ( root , text = None , justify = " center " )
status_label . place ( relx = 0.1 , rely = 0.9 , relwidth = 0.8 )
donate_label = ctk . CTkLabel ( root , text = ' Deep Live Cam ' , justify = ' center ' , cursor = ' hand2 ' )
donate_label = ctk . CTkLabel (
root , text = " Deep Live Cam " , justify = " center " , cursor = " hand2 "
)
donate_label . place ( relx = 0.1 , rely = 0.95 , relwidth = 0.8 )
donate_label . configure ( text_color = ctk . ThemeManager . theme . get ( ' URL ' ) . get ( ' text_color ' ) )
donate_label . bind ( ' <Button> ' , lambda event : webbrowser . open ( ' https://paypal.me/hacksider ' ) )
donate_label . configure (
text_color = ctk . ThemeManager . theme . get ( " URL " ) . get ( " text_color " )
)
donate_label . bind (
" <Button> " , lambda event : webbrowser . open ( " https://paypal.me/hacksider " )
)
return root
def analyze_target ( start : Callable [ [ ] , None ] , root : ctk . CTk ) :
if POPUP != None and POPUP . winfo_exists ( ) :
update_status ( " Please complete pop-up or close it. " )
@ -160,10 +294,10 @@ def analyze_target(start: Callable[[], None], root: ctk.CTk):
modules . globals . souce_target_map = [ ]
if is_image ( modules . globals . target_path ) :
update_status ( ' Getting unique faces ' )
update_status ( " Getting unique faces " )
get_unique_faces_from_target_image ( )
elif is_video ( modules . globals . target_path ) :
update_status ( ' Getting unique faces ' )
update_status ( " Getting unique faces " )
get_unique_faces_from_target_video ( )
if len ( modules . globals . souce_target_map ) > 0 :
@ -173,7 +307,10 @@ def analyze_target(start: Callable[[], None], root: ctk.CTk):
else :
select_output_path ( start )
def create_source_target_popup ( start : Callable [ [ ] , None ] , root : ctk . CTk , map : list ) - > None :
def create_source_target_popup (
start : Callable [ [ ] , None ] , root : ctk . CTk , map : list
) - > None :
global POPUP , popup_status_label
POPUP = ctk . CTkToplevel ( root )
@ -188,40 +325,74 @@ def create_source_target_popup(start: Callable[[], None], root: ctk.CTk, map: li
else :
update_pop_status ( " Atleast 1 source with target is required! " )
scrollable_frame = ctk . CTkScrollableFrame ( POPUP , width = POPUP_SCROLL_WIDTH , height = POPUP_SCROLL_HEIGHT )
scrollable_frame . grid ( row = 0 , column = 0 , padx = 0 , pady = 0 , sticky = ' nsew ' )
scrollable_frame = ctk . CTkScrollableFrame (
POPUP , width = POPUP_SCROLL_WIDTH , height = POPUP_SCROLL_HEIGHT
)
scrollable_frame . grid ( row = 0 , column = 0 , padx = 0 , pady = 0 , sticky = " nsew " )
def on_button_click ( map , button_num ) :
map = update_popup_source ( scrollable_frame , map , button_num )
update_popup_source ( scrollable_frame , map , button_num )
for item in map :
id = item [ ' id ' ]
button = ctk . CTkButton ( scrollable_frame , text = " Select source image " , command = lambda id = id : on_button_click ( map , id ) , width = DEFAULT_BUTTON_WIDTH , height = DEFAULT_BUTTON_HEIGHT )
id = item [ " id " ]
button = SourceMapperButton (
scrollable_frame ,
map ,
id ,
text = " Select source image " ,
command = lambda id = id : on_button_click ( map , id ) ,
width = DEFAULT_BUTTON_WIDTH ,
height = DEFAULT_BUTTON_HEIGHT ,
)
button . grid ( row = id , column = 0 , padx = 50 , pady = 10 )
x_label = ctk . CTkLabel ( scrollable_frame , text = f " X " , width = MAPPER_PREVIEW_MAX_WIDTH , height = MAPPER_PREVIEW_MAX_HEIGHT )
x_label = ctk . CTkLabel (
scrollable_frame ,
text = f " X " ,
width = MAPPER_PREVIEW_MAX_WIDTH ,
height = MAPPER_PREVIEW_MAX_HEIGHT ,
)
x_label . grid ( row = id , column = 2 , padx = 10 , pady = 10 )
image = Image . fromarray ( cv2 . cvtColor ( item [ ' target ' ] [ ' cv2 ' ] , cv2 . COLOR_BGR2RGB ) )
image = image . resize ( ( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS )
image = Image . fromarray ( cv2 . cvtColor ( item [ " target " ] [ " cv2 " ] , cv2 . COLOR_BGR2RGB ) )
image = image . resize (
( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS
)
tk_image = ctk . CTkImage ( image , size = image . size )
target_image = ctk . CTkLabel ( scrollable_frame , text = f " T- { id } " , width = MAPPER_PREVIEW_MAX_WIDTH , height = MAPPER_PREVIEW_MAX_HEIGHT )
target_image = ctk . CTkLabel (
scrollable_frame ,
text = f " T- { id } " ,
width = MAPPER_PREVIEW_MAX_WIDTH ,
height = MAPPER_PREVIEW_MAX_HEIGHT ,
)
target_image . grid ( row = id , column = 3 , padx = 10 , pady = 10 )
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 )
close_button = ctk . CTkButton ( POPUP , text = " Submit " , command = lambda : on_submit_click ( start ) )
close_button = ctk . CTkButton (
POPUP , text = " Submit " , command = lambda : on_submit_click ( start )
)
close_button . grid ( row = 2 , column = 0 , pady = 10 )
def update_popup_source ( scrollable_frame : ctk . CTkScrollableFrame , map : list , button_num : int ) - > list :
global source_label_dict
def update_popup_source (
scrollable_frame : ctk . CTkScrollableFrame ,
map : list ,
button_num : int ,
source_path : str = None ,
) - > list :
global source_label_dict , RECENT_DIRECTORY_SOURCE
source_path = ctk . filedialog . askopenfilename ( title = ' select an source image ' , initialdir = RECENT_DIRECTORY_SOURCE , filetypes = [ img_ft ] )
if source_path is None :
source_path = ctk . filedialog . askopenfilename (
title = " select an source image " ,
initialdir = RECENT_DIRECTORY_SOURCE ,
filetypes = [ img_ft ] ,
)
if " source " in map [ button_num ] :
map [ button_num ] . pop ( " source " )
@ -231,22 +402,32 @@ def update_popup_source(scrollable_frame: ctk.CTkScrollableFrame, map: list, but
if source_path == " " :
return map
else :
RECENT_DIRECTORY_SOURCE = os . path . dirname ( source_path )
cv2_img = cv2 . imread ( source_path )
face = get_one_face ( cv2_img )
if face :
x_min , y_min , x_max , y_max = face [ ' bbox ' ]
x_min , y_min , x_max , y_max = face [ " bbox " ]
map [ button_num ] [ ' source ' ] = {
' cv2 ' : cv2_img [ int ( y_min ) : int ( y_max ) , int ( x_min ) : int ( x_max ) ] ,
' face ' : face
map [ button_num ] [ " source " ] = {
" cv2 " : cv2_img [ int ( y_min ) : int ( y_max ) , int ( x_min ) : int ( x_max ) ] ,
" face " : face ,
}
image = Image . fromarray ( cv2 . cvtColor ( map [ button_num ] [ ' source ' ] [ ' cv2 ' ] , cv2 . COLOR_BGR2RGB ) )
image = image . resize ( ( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS )
image = Image . fromarray (
cv2 . cvtColor ( map [ button_num ] [ " source " ] [ " cv2 " ] , cv2 . COLOR_BGR2RGB )
)
image = image . resize (
( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS
)
tk_image = ctk . CTkImage ( image , size = image . size )
source_image = ctk . CTkLabel ( scrollable_frame , text = f " S- { button_num } " , width = MAPPER_PREVIEW_MAX_WIDTH , height = MAPPER_PREVIEW_MAX_HEIGHT )
source_image = ctk . CTkLabel (
scrollable_frame ,
text = f " S- { button_num } " ,
width = MAPPER_PREVIEW_MAX_WIDTH ,
height = MAPPER_PREVIEW_MAX_HEIGHT ,
)
source_image . grid ( row = button_num , column = 1 , padx = 10 , pady = 10 )
source_image . configure ( image = tk_image )
source_label_dict [ button_num ] = source_image
@ -260,15 +441,17 @@ def create_preview(parent: ctk.CTkToplevel) -> ctk.CTkToplevel:
preview = ctk . CTkToplevel ( parent )
preview . withdraw ( )
preview . title ( ' Preview ' )
preview . title ( " Preview " )
preview . configure ( )
preview . protocol ( ' WM_DELETE_WINDOW ' , lambda : toggle_preview ( ) )
preview . protocol ( " WM_DELETE_WINDOW " , lambda : toggle_preview ( ) )
preview . resizable ( width = True , height = True )
preview_label = ctk . CTkLabel ( preview , text = None )
preview_label . pack ( fill = ' both ' , expand = True )
preview_label . pack ( fill = " both " , expand = True )
preview_slider = ctk . CTkSlider ( preview , from_ = 0 , to = 0 , command = lambda frame_value : update_preview ( frame_value ) )
preview_slider = ctk . CTkSlider (
preview , from_ = 0 , to = 0 , command = lambda frame_value : update_preview ( frame_value )
)
return preview
@ -277,12 +460,15 @@ def update_status(text: str) -> None:
status_label . configure ( text = text )
ROOT . update ( )
def update_pop_status ( text : str ) - > None :
popup_status_label . configure ( text = text )
def update_pop_live_status ( text : str ) - > None :
popup_status_label_live . configure ( text = text )
def update_tumbler ( var : str , value : bool ) - > None :
modules . globals . fp_ui [ var ] = value
@ -291,7 +477,11 @@ def select_source_path() -> None:
global RECENT_DIRECTORY_SOURCE , img_ft , vid_ft
PREVIEW . withdraw ( )
source_path = ctk . filedialog . askopenfilename ( title = ' select an source image ' , initialdir = RECENT_DIRECTORY_SOURCE , filetypes = [ img_ft ] )
source_path = ctk . filedialog . askopenfilename (
title = " select an source image " ,
initialdir = RECENT_DIRECTORY_SOURCE ,
filetypes = [ img_ft ] ,
)
if is_image ( source_path ) :
modules . globals . source_path = source_path
RECENT_DIRECTORY_SOURCE = os . path . dirname ( modules . globals . source_path )
@ -330,7 +520,11 @@ def select_target_path() -> None:
global RECENT_DIRECTORY_TARGET , img_ft , vid_ft
PREVIEW . withdraw ( )
target_path = ctk . filedialog . askopenfilename ( title = ' select an target image or video ' , initialdir = RECENT_DIRECTORY_TARGET , filetypes = [ img_ft , vid_ft ] )
target_path = ctk . filedialog . askopenfilename (
title = " select an target image or video " ,
initialdir = RECENT_DIRECTORY_TARGET ,
filetypes = [ img_ft , vid_ft ] ,
)
if is_image ( target_path ) :
modules . globals . target_path = target_path
RECENT_DIRECTORY_TARGET = os . path . dirname ( modules . globals . target_path )
@ -350,9 +544,21 @@ def select_output_path(start: Callable[[], None]) -> None:
global RECENT_DIRECTORY_OUTPUT , img_ft , vid_ft
if is_image ( modules . globals . target_path ) :
output_path = ctk . filedialog . asksaveasfilename ( title = ' save image output file ' , filetypes = [ img_ft ] , defaultextension = ' .png ' , initialfile = ' output.png ' , initialdir = RECENT_DIRECTORY_OUTPUT )
output_path = ctk . filedialog . asksaveasfilename (
title = " save image output file " ,
filetypes = [ img_ft ] ,
defaultextension = " .png " ,
initialfile = " output.png " ,
initialdir = RECENT_DIRECTORY_OUTPUT ,
)
elif is_video ( modules . globals . target_path ) :
output_path = ctk . filedialog . asksaveasfilename ( title = ' save video output file ' , filetypes = [ vid_ft ] , defaultextension = ' .mp4 ' , initialfile = ' output.mp4 ' , initialdir = RECENT_DIRECTORY_OUTPUT )
output_path = ctk . filedialog . asksaveasfilename (
title = " save video output file " ,
filetypes = [ vid_ft ] ,
defaultextension = " .mp4 " ,
initialfile = " output.mp4 " ,
initialdir = RECENT_DIRECTORY_OUTPUT ,
)
else :
output_path = None
if output_path :
@ -362,20 +568,25 @@ def select_output_path(start: Callable[[], None]) -> None:
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 .
'''
"""
from numpy import ndarray
from modules . predicter import predict_image , predict_video , predict_frame
if type ( target ) is str : # image/video file path
check_nsfw = predict_image if has_image_extension ( target ) else predict_video
elif type ( target ) is ndarray : # frame object
check_nsfw = predict_frame
if check_nsfw and check_nsfw ( target ) :
if destroy : destroy ( to_quit = False ) # Do not need to destroy the window frame if the target is NSFW
update_status ( ' Processing ignored! ' )
if destroy :
destroy (
to_quit = False
) # Do not need to destroy the window frame if the target is NSFW
update_status ( " Processing ignored! " )
return True
else : return False
else :
return False
def fit_image_to_size ( image , width : int , height : int ) :
@ -400,7 +611,9 @@ def render_image_preview(image_path: str, size: Tuple[int, int]) -> ctk.CTkImage
return ctk . CTkImage ( image , size = image . size )
def render_video_preview ( video_path : str , size : Tuple [ int , int ] , frame_number : int = 0 ) - > ctk . CTkImage :
def render_video_preview (
video_path : str , size : Tuple [ int , int ] , frame_number : int = 0
) - > ctk . CTkImage :
capture = cv2 . VideoCapture ( video_path )
if frame_number :
capture . set ( cv2 . CAP_PROP_POS_FRAMES , frame_number )
@ -415,7 +628,7 @@ def render_video_preview(video_path: str, size: Tuple[int, int], frame_number: i
def toggle_preview ( ) - > None :
if PREVIEW . state ( ) == ' normal ' :
if PREVIEW . state ( ) == " normal " :
PREVIEW . withdraw ( )
elif modules . globals . source_path and modules . globals . target_path :
init_preview ( )
@ -428,28 +641,32 @@ def init_preview() -> None:
if is_video ( modules . globals . target_path ) :
video_frame_total = get_video_frame_total ( modules . globals . target_path )
preview_slider . configure ( to = video_frame_total )
preview_slider . pack ( fill = ' x ' )
preview_slider . pack ( fill = " x " )
preview_slider . set ( 0 )
def update_preview ( frame_number : int = 0 ) - > None :
if modules . globals . source_path and modules . globals . target_path :
update_status ( ' Processing... ' )
update_status ( " Processing... " )
temp_frame = get_video_frame ( modules . globals . target_path , frame_number )
if modules . globals . nsfw_filter and check_and_ignore_nsfw ( temp_frame ) :
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
) :
temp_frame = frame_processor . process_frame (
get_one_face ( cv2 . imread ( modules . globals . source_path ) ) ,
temp_frame
get_one_face ( cv2 . imread ( modules . globals . source_path ) ) , temp_frame
)
image = Image . fromarray ( cv2 . cvtColor ( temp_frame , cv2 . COLOR_BGR2RGB ) )
image = ImageOps . contain ( image , ( PREVIEW_MAX_WIDTH , PREVIEW_MAX_HEIGHT ) , Image . LANCZOS )
image = ImageOps . contain (
image , ( PREVIEW_MAX_WIDTH , PREVIEW_MAX_HEIGHT ) , Image . LANCZOS
)
image = ctk . CTkImage ( image , size = image . size )
preview_label . configure ( image = image )
update_status ( ' Processing succeed! ' )
update_status ( " Processing succeed! " )
PREVIEW . deiconify ( )
def webcam_preview ( root : ctk . CTk ) :
if not modules . globals . map_faces :
if modules . globals . source_path is None :
@ -460,15 +677,24 @@ def webcam_preview(root: ctk.CTk):
modules . globals . souce_target_map = [ ]
create_source_target_popup_for_webcam ( root , modules . globals . souce_target_map )
def create_webcam_preview ( ) :
global preview_label , PREVIEW
camera = cv2 . VideoCapture ( 0 ) # Use index for the webcam (adjust the index accordingly if necessary)
camera . set ( cv2 . CAP_PROP_FRAME_WIDTH , PREVIEW_DEFAULT_WIDTH ) # Set the width of the resolution
camera . set ( cv2 . CAP_PROP_FRAME_HEIGHT , PREVIEW_DEFAULT_HEIGHT ) # Set the height of the resolution
camera = cv2 . VideoCapture (
0
) # Use index for the webcam (adjust the index accordingly if necessary)
camera . set (
cv2 . CAP_PROP_FRAME_WIDTH , PREVIEW_DEFAULT_WIDTH
) # Set the width of the resolution
camera . set (
cv2 . CAP_PROP_FRAME_HEIGHT , PREVIEW_DEFAULT_HEIGHT
) # Set the height of the resolution
camera . set ( cv2 . CAP_PROP_FPS , 60 ) # Set the frame rate of the webcam
preview_label . configure ( width = PREVIEW_DEFAULT_WIDTH , height = PREVIEW_DEFAULT_HEIGHT ) # Reset the preview image before startup
preview_label . configure (
width = PREVIEW_DEFAULT_WIDTH , height = PREVIEW_DEFAULT_HEIGHT
) # Reset the preview image before startup
PREVIEW . deiconify ( ) # Open preview window
@ -481,13 +707,15 @@ def create_webcam_preview():
if not ret :
break
temp_frame = frame . copy ( ) # Create a copy of the frame
temp_frame = frame . copy ( ) # Create a copy of the frame
if modules . globals . live_mirror :
temp_frame = cv2 . flip ( temp_frame , 1 ) # horizontal flipping
if modules . globals . live_resizable :
temp_frame = fit_image_to_size ( temp_frame , PREVIEW . winfo_width ( ) , PREVIEW . winfo_height ( ) )
temp_frame = fit_image_to_size (
temp_frame , PREVIEW . winfo_width ( ) , PREVIEW . winfo_height ( )
)
if not modules . globals . map_faces :
# Select and save face image only once
@ -502,14 +730,18 @@ def create_webcam_preview():
for frame_processor in frame_processors :
temp_frame = frame_processor . process_frame_v2 ( temp_frame )
image = cv2 . cvtColor ( temp_frame , cv2 . COLOR_BGR2RGB ) # Convert the image to RGB format to display it with Tkinter
image = cv2 . cvtColor (
temp_frame , cv2 . COLOR_BGR2RGB
) # Convert the image to RGB format to display it with Tkinter
image = Image . fromarray ( image )
image = ImageOps . contain ( image , ( temp_frame . shape [ 1 ] , temp_frame . shape [ 0 ] ) , Image . LANCZOS )
image = ImageOps . contain (
image , ( temp_frame . shape [ 1 ] , temp_frame . shape [ 0 ] ) , Image . LANCZOS
)
image = ctk . CTkImage ( image , size = image . size )
preview_label . configure ( image = image )
ROOT . update ( )
if PREVIEW . state ( ) == ' withdrawn ' :
if PREVIEW . state ( ) == " withdrawn " :
break
camera . release ( )
@ -537,21 +769,25 @@ def create_source_target_popup_for_webcam(root: ctk.CTk, map: list) -> None:
refresh_data ( map )
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 ( POPUP_LIVE , text = None , justify = " center " )
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 . place ( relx = 0.2 , rely = 0.92 , relwidth = 0.2 , relheight = 0.05 )
close_button = ctk . CTkButton ( POPUP_LIVE , text = " Submit " , command = lambda : on_submit_click ( ) )
close_button = ctk . CTkButton (
POPUP_LIVE , text = " Submit " , command = lambda : on_submit_click ( )
)
close_button . place ( relx = 0.6 , rely = 0.92 , relwidth = 0.2 , relheight = 0.05 )
def refresh_data ( map : list ) :
global POPUP_LIVE
scrollable_frame = ctk . CTkScrollableFrame ( 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 = ctk . CTkScrollableFrame (
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 " )
def on_sbutton_click ( map , button_num ) :
map = update_webcam_source ( scrollable_frame , map , button_num )
@ -560,40 +796,81 @@ def refresh_data(map: list):
map = update_webcam_target ( scrollable_frame , map , button_num )
for item in map :
id = item [ ' id ' ]
button = ctk . CTkButton ( scrollable_frame , text = " Select source image " , command = lambda id = id : on_sbutton_click ( map , id ) , width = DEFAULT_BUTTON_WIDTH , height = DEFAULT_BUTTON_HEIGHT )
id = item [ " id " ]
button = ctk . CTkButton (
scrollable_frame ,
text = " Select source image " ,
command = lambda id = id : on_sbutton_click ( map , id ) ,
width = DEFAULT_BUTTON_WIDTH ,
height = DEFAULT_BUTTON_HEIGHT ,
)
button . grid ( row = id , column = 0 , padx = 30 , pady = 10 )
x_label = ctk . CTkLabel ( scrollable_frame , text = f " X " , width = MAPPER_PREVIEW_MAX_WIDTH , height = MAPPER_PREVIEW_MAX_HEIGHT )
x_label = ctk . CTkLabel (
scrollable_frame ,
text = f " X " ,
width = MAPPER_PREVIEW_MAX_WIDTH ,
height = MAPPER_PREVIEW_MAX_HEIGHT ,
)
x_label . grid ( row = id , column = 2 , padx = 10 , pady = 10 )
button = ctk . CTkButton ( scrollable_frame , text = " Select target image " , command = lambda id = id : on_tbutton_click ( map , id ) , width = DEFAULT_BUTTON_WIDTH , height = DEFAULT_BUTTON_HEIGHT )
button = ctk . CTkButton (
scrollable_frame ,
text = " Select target image " ,
command = lambda id = id : on_tbutton_click ( map , id ) ,
width = DEFAULT_BUTTON_WIDTH ,
height = DEFAULT_BUTTON_HEIGHT ,
)
button . grid ( row = id , column = 3 , padx = 20 , pady = 10 )
if " source " in item :
image = Image . fromarray ( cv2 . cvtColor ( item [ ' source ' ] [ ' cv2 ' ] , cv2 . COLOR_BGR2RGB ) )
image = image . resize ( ( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS )
image = Image . fromarray (
cv2 . cvtColor ( item [ " source " ] [ " cv2 " ] , cv2 . COLOR_BGR2RGB )
)
image = image . resize (
( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS
)
tk_image = ctk . CTkImage ( image , size = image . size )
source_image = ctk . CTkLabel ( scrollable_frame , text = f " S- { id } " , width = MAPPER_PREVIEW_MAX_WIDTH , height = MAPPER_PREVIEW_MAX_HEIGHT )
source_image = ctk . CTkLabel (
scrollable_frame ,
text = f " S- { id } " ,
width = MAPPER_PREVIEW_MAX_WIDTH ,
height = MAPPER_PREVIEW_MAX_HEIGHT ,
)
source_image . grid ( row = id , column = 1 , padx = 10 , pady = 10 )
source_image . configure ( image = tk_image )
if " target " in item :
image = Image . fromarray ( cv2 . cvtColor ( item [ ' target ' ] [ ' cv2 ' ] , cv2 . COLOR_BGR2RGB ) )
image = image . resize ( ( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS )
image = Image . fromarray (
cv2 . cvtColor ( item [ " target " ] [ " cv2 " ] , cv2 . COLOR_BGR2RGB )
)
image = image . resize (
( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS
)
tk_image = ctk . CTkImage ( image , size = image . size )
target_image = ctk . CTkLabel ( scrollable_frame , text = f " T- { id } " , width = MAPPER_PREVIEW_MAX_WIDTH , height = MAPPER_PREVIEW_MAX_HEIGHT )
target_image = ctk . CTkLabel (
scrollable_frame ,
text = f " T- { id } " ,
width = MAPPER_PREVIEW_MAX_WIDTH ,
height = MAPPER_PREVIEW_MAX_HEIGHT ,
)
target_image . grid ( row = id , column = 4 , padx = 20 , pady = 10 )
target_image . configure ( image = tk_image )
def update_webcam_source ( scrollable_frame : ctk . CTkScrollableFrame , map : list , button_num : int ) - > list :
def update_webcam_source (
scrollable_frame : ctk . CTkScrollableFrame , map : list , button_num : int
) - > list :
global source_label_dict_live
source_path = ctk . filedialog . askopenfilename ( title = ' select an source image ' , initialdir = RECENT_DIRECTORY_SOURCE , filetypes = [ img_ft ] )
source_path = ctk . filedialog . askopenfilename (
title = " select an source image " ,
initialdir = RECENT_DIRECTORY_SOURCE ,
filetypes = [ img_ft ] ,
)
if " source " in map [ button_num ] :
map [ button_num ] . pop ( " source " )
@ -607,18 +884,27 @@ def update_webcam_source(scrollable_frame: ctk.CTkScrollableFrame, map: list, bu
face = get_one_face ( cv2_img )
if face :
x_min , y_min , x_max , y_max = face [ ' bbox ' ]
x_min , y_min , x_max , y_max = face [ " bbox " ]
map [ button_num ] [ ' source ' ] = {
' cv2 ' : cv2_img [ int ( y_min ) : int ( y_max ) , int ( x_min ) : int ( x_max ) ] ,
' face ' : face
map [ button_num ] [ " source " ] = {
" cv2 " : cv2_img [ int ( y_min ) : int ( y_max ) , int ( x_min ) : int ( x_max ) ] ,
" face " : face ,
}
image = Image . fromarray ( cv2 . cvtColor ( map [ button_num ] [ ' source ' ] [ ' cv2 ' ] , cv2 . COLOR_BGR2RGB ) )
image = image . resize ( ( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS )
image = Image . fromarray (
cv2 . cvtColor ( map [ button_num ] [ " source " ] [ " cv2 " ] , cv2 . COLOR_BGR2RGB )
)
image = image . resize (
( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS
)
tk_image = ctk . CTkImage ( image , size = image . size )
source_image = ctk . CTkLabel ( scrollable_frame , text = f " S- { button_num } " , width = MAPPER_PREVIEW_MAX_WIDTH , height = MAPPER_PREVIEW_MAX_HEIGHT )
source_image = ctk . CTkLabel (
scrollable_frame ,
text = f " S- { button_num } " ,
width = MAPPER_PREVIEW_MAX_WIDTH ,
height = MAPPER_PREVIEW_MAX_HEIGHT ,
)
source_image . grid ( row = button_num , column = 1 , padx = 10 , pady = 10 )
source_image . configure ( image = tk_image )
source_label_dict_live [ button_num ] = source_image
@ -626,10 +912,17 @@ def update_webcam_source(scrollable_frame: ctk.CTkScrollableFrame, map: list, bu
update_pop_live_status ( " Face could not be detected in last upload! " )
return map
def update_webcam_target ( scrollable_frame : ctk . CTkScrollableFrame , map : list , button_num : int ) - > list :
def update_webcam_target (
scrollable_frame : ctk . CTkScrollableFrame , map : list , button_num : int
) - > list :
global target_label_dict_live
target_path = ctk . filedialog . askopenfilename ( title = ' select an target image ' , initialdir = RECENT_DIRECTORY_SOURCE , filetypes = [ img_ft ] )
target_path = ctk . filedialog . askopenfilename (
title = " select an target image " ,
initialdir = RECENT_DIRECTORY_SOURCE ,
filetypes = [ img_ft ] ,
)
if " target " in map [ button_num ] :
map [ button_num ] . pop ( " target " )
@ -643,22 +936,30 @@ def update_webcam_target(scrollable_frame: ctk.CTkScrollableFrame, map: list, bu
face = get_one_face ( cv2_img )
if face :
x_min , y_min , x_max , y_max = face [ ' bbox ' ]
x_min , y_min , x_max , y_max = face [ " bbox " ]
map [ button_num ] [ ' target ' ] = {
' cv2 ' : cv2_img [ int ( y_min ) : int ( y_max ) , int ( x_min ) : int ( x_max ) ] ,
' face ' : face
map [ button_num ] [ " target " ] = {
" cv2 " : cv2_img [ int ( y_min ) : int ( y_max ) , int ( x_min ) : int ( x_max ) ] ,
" face " : face ,
}
image = Image . fromarray ( cv2 . cvtColor ( map [ button_num ] [ ' target ' ] [ ' cv2 ' ] , cv2 . COLOR_BGR2RGB ) )
image = image . resize ( ( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS )
image = Image . fromarray (
cv2 . cvtColor ( map [ button_num ] [ " target " ] [ " cv2 " ] , cv2 . COLOR_BGR2RGB )
)
image = image . resize (
( MAPPER_PREVIEW_MAX_WIDTH , MAPPER_PREVIEW_MAX_HEIGHT ) , Image . LANCZOS
)
tk_image = ctk . CTkImage ( image , size = image . size )
target_image = ctk . CTkLabel ( scrollable_frame , text = f " T- { button_num } " , width = MAPPER_PREVIEW_MAX_WIDTH , height = MAPPER_PREVIEW_MAX_HEIGHT )
target_image = ctk . CTkLabel (
scrollable_frame ,
text = f " T- { button_num } " ,
width = MAPPER_PREVIEW_MAX_WIDTH ,
height = MAPPER_PREVIEW_MAX_HEIGHT ,
)
target_image . grid ( row = button_num , column = 4 , padx = 20 , pady = 10 )
target_image . configure ( image = tk_image )
target_label_dict_live [ button_num ] = target_image
else :
update_pop_live_status ( " Face could not be detected in last upload! " )
return map