Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add resolution override option to resize the frame to a given height … #21

Open
wants to merge 33 commits into
base: development
Choose a base branch
from
Open
Changes from 25 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6662f72
sort videos and faces lists alphanumerically
JaredTherriault Oct 2, 2024
de19ead
fix scrollwheel behaviour for linux and mac, allow scrolling paramete…
JaredTherriault Oct 2, 2024
505ecd5
Stop playback when attempting to jump frames via button or mouse wheel
JaredTherriault Oct 3, 2024
34b445d
Fix mouse wheel seeking on timeline for Unix
JaredTherriault Oct 3, 2024
40147f8
add avg fps entry so users can find the best settings for realtime pl…
JaredTherriault Oct 3, 2024
4120146
Merge pull request #6 from JaredTherriault/sort-videos-and-faces-lists
JaredTherriault Oct 4, 2024
4c44154
Merge pull request #7 from JaredTherriault/fix-unix-scrollwheel
JaredTherriault Oct 4, 2024
1c686f1
Merge pull request #8 from JaredTherriault/fix-unix-mouse-wheel-seeking
JaredTherriault Oct 4, 2024
e0d7d30
Merge pull request #9 from JaredTherriault/stop-playback-before-jumpi…
JaredTherriault Oct 4, 2024
bc41d04
Merge pull request #10 from JaredTherriault/add-avg-fps-entry
JaredTherriault Oct 4, 2024
66d0f26
Fix divide by zero issue
JaredTherriault Oct 4, 2024
7f93ee8
Merge pull request #11 from JaredTherriault/add-avg-fps-entry
JaredTherriault Oct 4, 2024
51c7694
Clamp values when creating restore mouth mask to ensure offsets don't…
JaredTherriault Oct 6, 2024
a733c32
Merge pull request #12 from JaredTherriault/fix-mouth-restore-offset-…
JaredTherriault Oct 6, 2024
f8c21c5
Parameterize history limit
JaredTherriault Oct 6, 2024
8c3580a
Merge pull request #13 from JaredTherriault/add-avg-fps-entry
JaredTherriault Oct 6, 2024
947c767
Add tooltip for video tiles with untruncated filename
JaredTherriault Oct 12, 2024
cbe0201
Update GUI.py
JaredTherriault Oct 19, 2024
bbc0940
Add tooltip for video tiles with untruncated filename
JaredTherriault Oct 12, 2024
d1bffa8
Merge branch 'dev-merge' into add-video-tooltips
JaredTherriault Oct 19, 2024
76e4f89
Merge pull request #14 from JaredTherriault/add-video-tooltips
JaredTherriault Oct 19, 2024
ffc9280
Update VideoManager.py
JaredTherriault Oct 19, 2024
3941320
Merge pull request #15 from JaredTherriault/fix-thread-time-list-floa…
JaredTherriault Oct 19, 2024
cbab52c
Merge pull request #16 from JaredTherriault/3x_source_faces_rows
JaredTherriault Oct 19, 2024
bad70a8
Use ffmpeg to extract interp frames
JaredTherriault Oct 21, 2024
427ad5f
Implement frame skip
JaredTherriault Oct 21, 2024
91e8c10
Fix frame skipping
JaredTherriault Oct 21, 2024
db8540f
Implement auto frame skip (draft)
JaredTherriault Oct 22, 2024
b42019a
Improve frame drop detection, add UI controls
JaredTherriault Oct 23, 2024
66b3345
Merge branch 'dev-merge' into arbitrary-playback-fps
JaredTherriault Oct 23, 2024
4ab97d7
Merge pull request #17 from JaredTherriault/arbitrary-playback-fps
JaredTherriault Oct 23, 2024
60b9414
Add resolution override option to resize the frame to a given height …
JaredTherriault Oct 24, 2024
5681798
Merge branch 'dev-merge' into display-resolution-options
JaredTherriault Oct 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 62 additions & 32 deletions rope/GUI.py
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@
from rope.Dicts import CAMERA_BACKENDS
from rope.FaceLandmarks import FaceLandmarks
from rope.FaceEditor import FaceEditor
from rope.Hovertip import RopeHovertip
import gc

class GUI(tk.Tk):
@@ -204,6 +205,13 @@ def load_shortcuts_from_json():
self.bind('<Key>', self.handle_key_press)
self.bind("<Return>", lambda event: self.focus_set())


@staticmethod
def bind_scroll_events(widget, callback):
widget.bind("<MouseWheel>",lambda event: callback(event, delta=-int(event.delta / 120))) # Windows
widget.bind("<Button-4>", lambda event: callback(event, delta=-1)) # Unix
widget.bind("<Button-5>", lambda event: callback(event, delta=1)) # Unix

def handle_key_press(self, event):
if isinstance(self.focus_get(), tk.Entry):
return
@@ -798,8 +806,8 @@ def apply_params_visibility_configuration(params_visibility=None, params_face_ed
# Input Videos Canvas
self.target_media_canvas = tk.Canvas(self.layer['InputVideoFrame'], style.canvas_frame_label_3, height=100, width=195)
self.target_media_canvas.grid(row=1, column=0, sticky='NEWS', padx=10, pady=10)
self.target_media_canvas.bind("<MouseWheel>", self.target_videos_mouse_wheel)
self.target_media_canvas.create_text(8, 20, anchor='w', fill='grey25', font=("Arial italic", 20), text=" Input Videos")
self.bind_scroll_events(self.target_media_canvas, self.target_videos_mouse_wheel)

# Scroll Canvas
scroll_canvas = tk.Canvas(self.layer['InputVideoFrame'], style.canvas_frame_label_3, bd=0, )
@@ -821,7 +829,9 @@ def apply_params_visibility_configuration(params_visibility=None, params_face_ed
# Scroll Canvas
self.source_faces_canvas = tk.Canvas(self.layer['InputVideoFrame'], style.canvas_frame_label_3, height = 100, width=195)
self.source_faces_canvas.grid(row=1, column=2, sticky='NEWS', padx=10, pady=10)
self.source_faces_canvas.bind("<MouseWheel>", self.source_faces_mouse_wheel)

self.bind_scroll_events(self.source_faces_canvas, self.source_faces_mouse_wheel)

self.source_faces_canvas.create_text(8, 20, anchor='w', fill='grey25', font=("Arial italic", 20), text=" Input Faces")

scroll_canvas = tk.Canvas(self.layer['InputVideoFrame'], style.canvas_frame_label_3, bd=0, )
@@ -881,7 +891,7 @@ def apply_params_visibility_configuration(params_visibility=None, params_face_ed
# Preview Window
self.video = tk.Label(self.layer['preview_column'], bg='black')
self.video.grid(row=1, column=0, sticky='NEWS', padx=0, pady=0)
self.video.bind("<MouseWheel>", self.iterate_through_merged_embeddings)
self.bind_scroll_events(self.video, self.iterate_through_merged_embeddings)
self.video.bind("<ButtonRelease-1>", lambda event: self.toggle_play_video())

# Videos
@@ -977,7 +987,7 @@ def apply_params_visibility_configuration(params_visibility=None, params_face_ed
# Scroll Canvas
self.found_faces_canvas = tk.Canvas(ff_frame, style.canvas_frame_label_3, height = 100 )
self.found_faces_canvas.grid( row = 0, column = 1, sticky='NEWS')
self.found_faces_canvas.bind("<MouseWheel>", self.target_faces_mouse_wheel)
self.bind_scroll_events(self.found_faces_canvas, self.target_faces_mouse_wheel)
self.found_faces_canvas.create_text(8, 45, anchor='w', fill='grey25', font=("Arial italic", 20), text=" Found Faces")

self.static_widget['23'] = GE.Separator_y(ff_frame, 111, 0)
@@ -1006,7 +1016,7 @@ def apply_params_visibility_configuration(params_visibility=None, params_face_ed
self.merged_faces_canvas = tk.Canvas(mf_frame, style.canvas_frame_label_3, height = 100)
self.merged_faces_canvas.grid( row = 0, column = 1, sticky='NEWS')
self.merged_faces_canvas.grid_rowconfigure(0, weight=1)
self.merged_faces_canvas.bind("<MouseWheel>", lambda event: self.merged_faces_canvas.xview_scroll(-int(event.delta/120.0), "units"))
self.bind_scroll_events(self.merged_faces_canvas, lambda event, delta: self.merged_faces_canvas.xview_scroll(delta, "units"))
self.merged_faces_canvas.create_text(8, 45, anchor='w', fill='grey25', font=("Arial italic", 20), text=" Merged Faces")
self.static_widget['24'] = GE.Separator_y(mf_frame, 111, 0)

@@ -1042,10 +1052,11 @@ def apply_params_visibility_configuration(params_visibility=None, params_face_ed
self.widget['DefaultParamsButton'] = GE.Button(frame, 'DefaultParamsButton', 2, self.parameter_io, 'default', 'control', x=0 , y=8, width=100)

self.layer['parameters_canvas'] = tk.Canvas(self.layer['parameter_frame'], style.canvas_frame_label_3, bd=0, width=width)
self.layer['parameters_canvas'].grid(row=1, column=0, sticky='NEWS', pady=0, padx=0)
self.parameters_canvas = self.layer['parameters_canvas']
self.parameters_canvas.grid(row=1, column=0, sticky='NEWS', pady=0, padx=0)

# Face Editor
tabview_main = ctk.CTkTabview(self.layer['parameters_canvas'], width=398, height=2050, corner_radius=6, border_width=1,
tabview_main = ctk.CTkTabview(self.parameters_canvas, width=398, height=2050, corner_radius=6, border_width=1,
fg_color=style.main, border_color=style.main3,
segmented_button_selected_hover_color='#b1b1b2',
segmented_button_unselected_hover_color=style.main,
@@ -1057,7 +1068,7 @@ def apply_params_visibility_configuration(params_visibility=None, params_face_ed
tabview_main.pack(fill='both', expand=True) # Utilizza pack per gestire il layout all'interno del Canvas

# Inserisci il CTkTabview nel Canvas usando create_window
self.layer['parameters_canvas'].create_window(0, 0, window=tabview_main, anchor='nw')
self.parameters_canvas.create_window(0, 0, window=tabview_main, anchor='nw')

# Aggiungi Tabs al CTkTabview
tab_face_swapper = tabview_main.add("Face Swapper ")
@@ -1070,11 +1081,14 @@ def apply_params_visibility_configuration(params_visibility=None, params_face_ed
self.layer['parameters_face_editor_frame'].grid(row=0, column=0, sticky='NEWS', pady=0, padx=0)

self.layer['parameter_scroll_canvas'] = tk.Canvas(self.layer['parameter_frame'], style.canvas_frame_label_3, bd=0, )
self.layer['parameter_scroll_canvas'].grid(row=1, column=1, sticky='NEWS', pady=0)
self.layer['parameter_scroll_canvas'].grid_rowconfigure(0, weight=1)
self.layer['parameter_scroll_canvas'].grid_columnconfigure(0, weight=1)
parameter_scroll_canvas = self.layer['parameter_scroll_canvas']
parameter_scroll_canvas.grid(row=1, column=1, sticky='NEWS', pady=0)
parameter_scroll_canvas.grid_rowconfigure(0, weight=1)
parameter_scroll_canvas.grid_columnconfigure(0, weight=1)

self.static_widget['parameters_scrollbar'] = GE.Scrollbar_y(parameter_scroll_canvas, self.parameters_canvas)

self.static_widget['parameters_scrollbar'] = GE.Scrollbar_y(self.layer['parameter_scroll_canvas'], self.layer['parameters_canvas'])
self.bind_scroll_events(parameter_scroll_canvas, self.parameters_mouse_wheel)

self.static_widget['30'] = GE.Separator_x(parameters_control_frame, 0, 41)

@@ -1676,27 +1690,32 @@ def update_param_visibility(self, name, visible):
def callback(self, url):
webbrowser.open_new_tab(url)

def target_faces_mouse_wheel(self, event):
self.found_faces_canvas.xview_scroll(1*int(event.delta/120.0), "units")
def target_faces_mouse_wheel(self, event, delta = 0):
self.found_faces_canvas.xview_scroll(delta, "units")

def source_faces_mouse_wheel(self, event):
self.source_faces_canvas.yview_scroll(-int(event.delta/120.0), "units")
def source_faces_mouse_wheel(self, event, delta=0):
self.source_faces_canvas.yview_scroll(delta, "units")

# Center of visible canvas as a percentage of the entire canvas
center = (self.source_faces_canvas.yview()[1]-self.source_faces_canvas.yview()[0])/2
center = center+self.source_faces_canvas.yview()[0]
self.static_widget['input_faces_scrollbar'].set(center)

def target_videos_mouse_wheel(self, event):
self.target_media_canvas.yview_scroll(-int(event.delta/120.0), "units")
def target_videos_mouse_wheel(self, event, delta = 0):
self.target_media_canvas.yview_scroll(delta, "units")

# Center of visible canvas as a percentage of the entire canvas
center = (self.target_media_canvas.yview()[1]-self.target_media_canvas.yview()[0])/2
center = center+self.target_media_canvas.yview()[0]
self.static_widget['input_videos_scrollbar'].set(center)

def parameters_mouse_wheel(self, event):
self.canvas.yview_scroll(1*int(event.delta/120.0), "units")
def parameters_mouse_wheel(self, event, delta = 0):
self.parameters_canvas.yview_scroll(delta, "units")

# Center of visible canvas as a percentage of the entire canvas
center = (self.parameters_canvas.yview()[1]-self.parameters_canvas.yview()[0])/2
center = center+self.parameters_canvas.yview()[0]
self.static_widget['parameters_scrollbar'].set(center)

# focus_get()
# def preview_control(self, event):
@@ -1777,34 +1796,39 @@ def preview_control(self, event):
if event == ' ':
self.toggle_play_video()
elif event == 'w':
self.add_action("play_video", "stop")
frame += 1
if frame > video_length:
frame = video_length
self.video_slider.set(frame)
self.add_action("get_requested_video_frame", frame)
# self.parameter_update_from_marker(frame)
elif event == 's':
self.add_action("play_video", "stop")
frame -= 1
if frame < 0:
frame = 0
self.video_slider.set(frame)
self.add_action("get_requested_video_frame", frame)
# self.parameter_update_from_marker(frame)
elif event == 'd':
self.add_action("play_video", "stop")
frame += 30
if frame > video_length:
frame = video_length
self.video_slider.set(frame)
self.add_action("get_requested_video_frame", frame)
# self.parameter_update_from_marker(frame)
elif event == 'a':
self.add_action("play_video", "stop")
frame -= 30
if frame < 0:
frame = 0
self.video_slider.set(frame)
self.add_action("get_requested_video_frame", frame)
# self.parameter_update_from_marker(frame)
elif event == 'q':
self.add_action("play_video", "stop")
frame = 0
self.video_slider.set(frame)
self.add_action("get_requested_video_frame", frame)
@@ -2029,7 +2053,7 @@ def load_dfl_input_models(self):
new_source_face["TKButton"] = tk.Button(self.merged_faces_canvas, style.media_button_off_3, image=self.blank, text=button_text, height=14, width=text_width, compound='left', anchor='w')

new_source_face["TKButton"].bind("<ButtonRelease-1>", lambda event, arg=j: self.select_input_faces(event, arg))
new_source_face["TKButton"].bind("<MouseWheel>", lambda event: self.merged_faces_canvas.xview_scroll(-int(event.delta/120.0), "units"))
self.bind_scroll_events(new_source_face["TKButton"], lambda event, delta: self.merged_faces_canvas.xview_scroll(delta, "units"))
new_source_face['TextWidth'] = text_width
x_width = 20
if len(self.source_faces)>0:
@@ -2078,7 +2102,7 @@ def load_input_faces(self):
new_source_face["TKButton"] = tk.Button(self.merged_faces_canvas, style.media_button_off_3, image=self.blank, text=temp0[j][0], height=14, width=text_width, compound='left', anchor='w')

new_source_face["TKButton"].bind("<ButtonRelease-1>", lambda event, arg=j: self.select_input_faces(event, arg))
new_source_face["TKButton"].bind("<MouseWheel>", lambda event: self.merged_faces_canvas.xview_scroll(-int(event.delta/120.0), "units"))
self.bind_scroll_events(new_source_face["TKButton"], lambda event, delta: self.merged_faces_canvas.xview_scroll(delta, "units"))
new_source_face['TextWidth'] = text_width
x_width = 20
if len(self.source_faces)>0:
@@ -2101,6 +2125,8 @@ def load_input_faces(self):
directory = self.json_dict["source faces"]
filenames = [os.path.join(dirpath,f) for (dirpath, dirnames, filenames) in os.walk(directory) for f in filenames]

filenames = sorted(filenames, key=str.lower)

# torch.cuda.memory._record_memory_history(True, trace_alloc_max_entries=100000, trace_alloc_record_context=True)
i=0
for file in filenames: # Does not include full path
@@ -2145,21 +2171,21 @@ def load_input_faces(self):
else:
face_emb, cropped_image = self.models.run_recognize(img, kpss_5, self.parameters["SimilarityTypeTextSel"], self.parameters['FaceSwapperModelTextSel'])
crop = cv2.cvtColor(cropped_image.cpu().numpy(), cv2.COLOR_BGR2RGB)
crop = cv2.resize(crop, (85, 85))
crop = cv2.resize(crop, (50, 50))

new_source_face = self.source_face.copy()
self.source_faces.append(new_source_face)

self.source_faces[-1]["Image"] = ImageTk.PhotoImage(image=Image.fromarray(crop))
self.source_faces[-1]["Embedding"] = face_emb
self.source_faces[-1]["TKButton"] = tk.Button(self.source_faces_canvas, style.media_button_off_3, image=self.source_faces[-1]["Image"], height=90, width=90)
self.source_faces[-1]["TKButton"] = tk.Button(self.source_faces_canvas, style.media_button_off_3, image=self.source_faces[-1]["Image"], height=55, width=55)
self.source_faces[-1]["ButtonState"] = False
self.source_faces[-1]["file"] = file

self.source_faces[-1]["TKButton"].bind("<ButtonRelease-1>", lambda event, arg=len(self.source_faces)-1: self.select_input_faces(event, arg))
self.source_faces[-1]["TKButton"].bind("<MouseWheel>", self.source_faces_mouse_wheel)
self.bind_scroll_events(self.source_faces[-1]["TKButton"], self.source_faces_mouse_wheel)

self.source_faces_canvas.create_window((i % 2) * 100, (i // 2) * 100, window=self.source_faces[-1]["TKButton"], anchor='nw')
self.source_faces_canvas.create_window((i % 3) * 65, (i // 3) * 65, window=self.source_faces[-1]["TKButton"], anchor='nw')

self.static_widget['input_faces_scrollbar'].resize_scrollbar(None)
i = i + 1
@@ -2216,7 +2242,7 @@ def find_faces(self):
last_index = len(self.target_faces)-1

self.target_faces[last_index]["TKButton"] = tk.Button(self.found_faces_canvas, style.media_button_off_3, height = 86, width = 86)
self.target_faces[last_index]["TKButton"].bind("<MouseWheel>", self.target_faces_mouse_wheel)
self.bind_scroll_events(self.target_faces[last_index]["TKButton"], self.target_faces_mouse_wheel)
self.target_faces[last_index]["ButtonState"] = False
self.target_faces[last_index]["Image"] = ImageTk.PhotoImage(image=Image.fromarray(crop))
self.target_faces[last_index]["Embedding"] = face[1]
@@ -2396,6 +2422,8 @@ def populate_target_videos(self):
directory = self.json_dict["source videos"]
filenames = [os.path.join(dirpath,f) for (dirpath, dirnames, filenames) in os.walk(directory) for f in filenames]

filenames = sorted(filenames, key=str.lower)

images = []
self.target_media = []
self.target_media_buttons = []
@@ -2461,7 +2489,7 @@ def populate_target_videos(self):
rgb_video = Image.fromarray(images[i][0])
self.target_media.append(ImageTk.PhotoImage(image=rgb_video))
self.target_media_buttons[i].config( image = self.target_media[i], command=lambda i=i: self.load_target(i, images[i][1], self.widget['PreviewModeTextSel'].get()))
self.target_media_buttons[i].bind("<MouseWheel>", self.target_videos_mouse_wheel)
self.bind_scroll_events(self.target_media_buttons[i], self.target_videos_mouse_wheel)
self.target_media_canvas.create_window(0, i*dely, window = self.target_media_buttons[i], anchor='nw')

#self.target_media_canvas.configure(scrollregion = self.target_media_canvas.bbox("all"))
@@ -2474,10 +2502,11 @@ def populate_target_videos(self):
self.target_media.append(ImageTk.PhotoImage(image=Image.fromarray(videos[i][0])))

filename = os.path.basename(videos[i][1])
hovertip = RopeHovertip(self.target_media_buttons[i], filename, x_offset=190)
if len(filename)>32:
filename = filename[:29]+'...'

self.target_media_buttons[i].bind("<MouseWheel>", self.target_videos_mouse_wheel)
self.bind_scroll_events(self.target_media_buttons[i], self.target_videos_mouse_wheel)
self.target_media_buttons[i].config(image = self.target_media[i], text=filename, compound='top', anchor='n',command=lambda i=i: self.load_target(i, videos[i][1], self.widget['PreviewModeTextSel'].get()))
self.target_media_canvas.create_window(0, i*dely, window = self.target_media_buttons[i], anchor='nw')

@@ -2497,6 +2526,7 @@ def auto_swap(self):
pass

def toggle_auto_swap(self):
print("toggle auto swap")
self.widget['AutoSwapButton'].toggle_button()

def load_target(self, button, media_file, media_type):
@@ -2879,13 +2909,13 @@ def delete_merged_embedding(self): #add multi select

self.load_input_faces()

def iterate_through_merged_embeddings(self, event):
if event.delta>0:
def iterate_through_merged_embeddings(self, event, delta):
if delta>0:
for i in range(len(self.source_faces)):
if self.source_faces[i]["ButtonState"] and i<len(self.source_faces)-1:
self.select_input_faces('none', i+1)
break
elif event.delta<0:
elif delta<0:
for i in range(len(self.source_faces)):
if self.source_faces[i]["ButtonState"]and i>0:
self.select_input_faces('none', i-1)
Loading