aiguruji97 commited on
Commit
5246eab
·
verified ·
1 Parent(s): 79f204b

Update roop/ui.py

Browse files
Files changed (1) hide show
  1. roop/ui.py +295 -285
roop/ui.py CHANGED
@@ -1,285 +1,295 @@
1
- import os
2
- import sys
3
- import webbrowser
4
- import customtkinter as ctk
5
- from tkinterdnd2 import TkinterDnD, DND_ALL
6
- from typing import Any, Callable, Tuple, Optional
7
- import cv2
8
- from PIL import Image, ImageOps
9
-
10
- import roop.globals
11
- import roop.metadata
12
- from roop.face_analyser import get_one_face
13
- from roop.capturer import get_video_frame, get_video_frame_total
14
- from roop.face_reference import get_face_reference, set_face_reference, clear_face_reference
15
- from roop.predictor import predict_frame, clear_predictor
16
- from roop.processors.frame.core import get_frame_processors_modules
17
- from roop.utilities import is_image, is_video, resolve_relative_path
18
-
19
- ROOT = None
20
- ROOT_HEIGHT = 700
21
- ROOT_WIDTH = 600
22
-
23
- PREVIEW = None
24
- PREVIEW_MAX_HEIGHT = 700
25
- PREVIEW_MAX_WIDTH = 1200
26
-
27
- RECENT_DIRECTORY_SOURCE = None
28
- RECENT_DIRECTORY_TARGET = None
29
- RECENT_DIRECTORY_OUTPUT = None
30
-
31
- preview_label = None
32
- preview_slider = None
33
- source_label = None
34
- target_label = None
35
- status_label = None
36
-
37
-
38
- # todo: remove by native support -> https://github.com/TomSchimansky/CustomTkinter/issues/934
39
- class CTk(ctk.CTk, TkinterDnD.DnDWrapper):
40
- def __init__(self, *args: Any, **kwargs: Any) -> None:
41
- super().__init__(*args, **kwargs)
42
- self.TkdndVersion = TkinterDnD._require(self)
43
-
44
-
45
- def init(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk:
46
- global ROOT, PREVIEW
47
-
48
- ROOT = create_root(start, destroy)
49
- PREVIEW = create_preview(ROOT)
50
-
51
- return ROOT
52
-
53
-
54
- def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk:
55
- global source_label, target_label, status_label
56
-
57
- ctk.deactivate_automatic_dpi_awareness()
58
- ctk.set_appearance_mode('system')
59
- ctk.set_default_color_theme(resolve_relative_path('ui.json'))
60
-
61
- root = CTk()
62
- root.minsize(ROOT_WIDTH, ROOT_HEIGHT)
63
- root.title(f'{roop.metadata.name} {roop.metadata.version}')
64
- root.configure()
65
- root.protocol('WM_DELETE_WINDOW', lambda: destroy())
66
-
67
- source_label = ctk.CTkLabel(root, text=None, fg_color=ctk.ThemeManager.theme.get('RoopDropArea').get('fg_color'))
68
- source_label.place(relx=0.1, rely=0.1, relwidth=0.3, relheight=0.25)
69
- source_label.drop_target_register(DND_ALL)
70
- source_label.dnd_bind('<<Drop>>', lambda event: select_source_path(event.data))
71
- if roop.globals.source_path:
72
- select_source_path(roop.globals.source_path)
73
-
74
- target_label = ctk.CTkLabel(root, text=None, fg_color=ctk.ThemeManager.theme.get('RoopDropArea').get('fg_color'))
75
- target_label.place(relx=0.6, rely=0.1, relwidth=0.3, relheight=0.25)
76
- target_label.drop_target_register(DND_ALL)
77
- target_label.dnd_bind('<<Drop>>', lambda event: select_target_path(event.data))
78
- if roop.globals.target_path:
79
- select_target_path(roop.globals.target_path)
80
-
81
- source_button = ctk.CTkButton(root, text='Select a face', cursor='hand2', command=lambda: select_source_path())
82
- source_button.place(relx=0.1, rely=0.4, relwidth=0.3, relheight=0.1)
83
-
84
- target_button = ctk.CTkButton(root, text='Select a target', cursor='hand2', command=lambda: select_target_path())
85
- target_button.place(relx=0.6, rely=0.4, relwidth=0.3, relheight=0.1)
86
-
87
- keep_fps_value = ctk.BooleanVar(value=roop.globals.keep_fps)
88
- keep_fps_checkbox = ctk.CTkSwitch(root, text='Keep target fps', variable=keep_fps_value, cursor='hand2', command=lambda: setattr(roop.globals, 'keep_fps', not roop.globals.keep_fps))
89
- keep_fps_checkbox.place(relx=0.1, rely=0.6)
90
-
91
- keep_frames_value = ctk.BooleanVar(value=roop.globals.keep_frames)
92
- keep_frames_switch = ctk.CTkSwitch(root, text='Keep temporary frames', variable=keep_frames_value, cursor='hand2', command=lambda: setattr(roop.globals, 'keep_frames', keep_frames_value.get()))
93
- keep_frames_switch.place(relx=0.1, rely=0.65)
94
-
95
- skip_audio_value = ctk.BooleanVar(value=roop.globals.skip_audio)
96
- skip_audio_switch = ctk.CTkSwitch(root, text='Skip target audio', variable=skip_audio_value, cursor='hand2', command=lambda: setattr(roop.globals, 'skip_audio', skip_audio_value.get()))
97
- skip_audio_switch.place(relx=0.6, rely=0.6)
98
-
99
- many_faces_value = ctk.BooleanVar(value=roop.globals.many_faces)
100
- many_faces_switch = ctk.CTkSwitch(root, text='Many faces', variable=many_faces_value, cursor='hand2', command=lambda: setattr(roop.globals, 'many_faces', many_faces_value.get()))
101
- many_faces_switch.place(relx=0.6, rely=0.65)
102
-
103
- start_button = ctk.CTkButton(root, text='Start', cursor='hand2', command=lambda: select_output_path(start))
104
- start_button.place(relx=0.15, rely=0.75, relwidth=0.2, relheight=0.05)
105
-
106
- stop_button = ctk.CTkButton(root, text='Destroy', cursor='hand2', command=lambda: destroy())
107
- stop_button.place(relx=0.4, rely=0.75, relwidth=0.2, relheight=0.05)
108
-
109
- preview_button = ctk.CTkButton(root, text='Preview', cursor='hand2', command=lambda: toggle_preview())
110
- preview_button.place(relx=0.65, rely=0.75, relwidth=0.2, relheight=0.05)
111
-
112
- status_label = ctk.CTkLabel(root, text=None, justify='center')
113
- status_label.place(relx=0.1, rely=0.9, relwidth=0.8)
114
-
115
- donate_label = ctk.CTkLabel(root, text='^_^ Donate to project ^_^', justify='center', cursor='hand2')
116
- donate_label.place(relx=0.1, rely=0.95, relwidth=0.8)
117
- donate_label.configure(text_color=ctk.ThemeManager.theme.get('RoopDonate').get('text_color'))
118
- donate_label.bind('<Button>', lambda event: webbrowser.open('https://github.com/sponsors/s0md3v'))
119
-
120
- return root
121
-
122
-
123
- def create_preview(parent: ctk.CTkToplevel) -> ctk.CTkToplevel:
124
- global preview_label, preview_slider
125
-
126
- preview = ctk.CTkToplevel(parent)
127
- preview.withdraw()
128
- preview.configure()
129
- preview.protocol('WM_DELETE_WINDOW', lambda: toggle_preview())
130
- preview.resizable(width=False, height=False)
131
-
132
- preview_label = ctk.CTkLabel(preview, text=None)
133
- preview_label.pack(fill='both', expand=True)
134
-
135
- preview_slider = ctk.CTkSlider(preview, from_=0, to=0, command=lambda frame_value: update_preview(frame_value))
136
-
137
- preview.bind('<Up>', lambda event: update_face_reference(1))
138
- preview.bind('<Down>', lambda event: update_face_reference(-1))
139
- return preview
140
-
141
-
142
- def update_status(text: str) -> None:
143
- status_label.configure(text=text)
144
- ROOT.update()
145
-
146
-
147
- def select_source_path(source_path: Optional[str] = None) -> None:
148
- global RECENT_DIRECTORY_SOURCE
149
-
150
- if PREVIEW:
151
- PREVIEW.withdraw()
152
- if source_path is None:
153
- source_path = ctk.filedialog.askopenfilename(title='select an source image', initialdir=RECENT_DIRECTORY_SOURCE)
154
- if is_image(source_path):
155
- roop.globals.source_path = source_path
156
- RECENT_DIRECTORY_SOURCE = os.path.dirname(roop.globals.source_path)
157
- image = render_image_preview(roop.globals.source_path, (200, 200))
158
- source_label.configure(image=image)
159
- else:
160
- roop.globals.source_path = None
161
- source_label.configure(image=None)
162
-
163
-
164
- def select_target_path(target_path: Optional[str] = None) -> None:
165
- global RECENT_DIRECTORY_TARGET
166
-
167
- if PREVIEW:
168
- PREVIEW.withdraw()
169
- clear_face_reference()
170
- if target_path is None:
171
- target_path = ctk.filedialog.askopenfilename(title='select an target image or video', initialdir=RECENT_DIRECTORY_TARGET)
172
- if is_image(target_path):
173
- roop.globals.target_path = target_path
174
- RECENT_DIRECTORY_TARGET = os.path.dirname(roop.globals.target_path)
175
- image = render_image_preview(roop.globals.target_path, (200, 200))
176
- target_label.configure(image=image)
177
- elif is_video(target_path):
178
- roop.globals.target_path = target_path
179
- RECENT_DIRECTORY_TARGET = os.path.dirname(roop.globals.target_path)
180
- video_frame = render_video_preview(target_path, (200, 200))
181
- target_label.configure(image=video_frame)
182
- else:
183
- roop.globals.target_path = None
184
- target_label.configure(image=None)
185
-
186
-
187
- def select_output_path(start: Callable[[], None]) -> None:
188
- global RECENT_DIRECTORY_OUTPUT
189
-
190
- if is_image(roop.globals.target_path):
191
- output_path = ctk.filedialog.asksaveasfilename(title='save image output file', defaultextension='.png', initialfile='output.png', initialdir=RECENT_DIRECTORY_OUTPUT)
192
- elif is_video(roop.globals.target_path):
193
- output_path = ctk.filedialog.asksaveasfilename(title='save video output file', defaultextension='.mp4', initialfile='output.mp4', initialdir=RECENT_DIRECTORY_OUTPUT)
194
- else:
195
- output_path = None
196
- if output_path:
197
- roop.globals.output_path = output_path
198
- RECENT_DIRECTORY_OUTPUT = os.path.dirname(roop.globals.output_path)
199
- start()
200
-
201
-
202
- def render_image_preview(image_path: str, size: Tuple[int, int]) -> ctk.CTkImage:
203
- image = Image.open(image_path)
204
- if size:
205
- image = ImageOps.fit(image, size, Image.LANCZOS)
206
- return ctk.CTkImage(image, size=image.size)
207
-
208
-
209
- def render_video_preview(video_path: str, size: Tuple[int, int], frame_number: int = 0) -> ctk.CTkImage:
210
- capture = cv2.VideoCapture(video_path)
211
- if frame_number:
212
- capture.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
213
- has_frame, frame = capture.read()
214
- if has_frame:
215
- image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
216
- if size:
217
- image = ImageOps.fit(image, size, Image.LANCZOS)
218
- return ctk.CTkImage(image, size=image.size)
219
- capture.release()
220
- cv2.destroyAllWindows()
221
-
222
-
223
- def toggle_preview() -> None:
224
- if PREVIEW.state() == 'normal':
225
- PREVIEW.unbind('<Right>')
226
- PREVIEW.unbind('<Left>')
227
- PREVIEW.withdraw()
228
- clear_predictor()
229
- elif roop.globals.source_path and roop.globals.target_path:
230
- init_preview()
231
- update_preview(roop.globals.reference_frame_number)
232
- PREVIEW.deiconify()
233
-
234
-
235
- def init_preview() -> None:
236
- PREVIEW.title('Preview [ ↕ Reference face ]')
237
- if is_image(roop.globals.target_path):
238
- preview_slider.pack_forget()
239
- if is_video(roop.globals.target_path):
240
- video_frame_total = get_video_frame_total(roop.globals.target_path)
241
- if video_frame_total > 0:
242
- PREVIEW.title('Preview [ ↕ Reference face ] [ ↔ Frame number ]')
243
- PREVIEW.bind('<Right>', lambda event: update_frame(int(video_frame_total / 20)))
244
- PREVIEW.bind('<Left>', lambda event: update_frame(int(video_frame_total / -20)))
245
- preview_slider.configure(to=video_frame_total)
246
- preview_slider.pack(fill='x')
247
- preview_slider.set(roop.globals.reference_frame_number)
248
-
249
-
250
- def update_preview(frame_number: int = 0) -> None:
251
- if roop.globals.source_path and roop.globals.target_path:
252
- temp_frame = get_video_frame(roop.globals.target_path, frame_number)
253
- if predict_frame(temp_frame):
254
- sys.exit()
255
- source_face = get_one_face(cv2.imread(roop.globals.source_path))
256
- if not get_face_reference():
257
- reference_frame = get_video_frame(roop.globals.target_path, roop.globals.reference_frame_number)
258
- reference_face = get_one_face(reference_frame, roop.globals.reference_face_position)
259
- set_face_reference(reference_face)
260
- else:
261
- reference_face = get_face_reference()
262
- for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
263
- temp_frame = frame_processor.process_frame(
264
- source_face,
265
- reference_face,
266
- temp_frame
267
- )
268
- image = Image.fromarray(cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB))
269
- image = ImageOps.contain(image, (PREVIEW_MAX_WIDTH, PREVIEW_MAX_HEIGHT), Image.LANCZOS)
270
- image = ctk.CTkImage(image, size=image.size)
271
- preview_label.configure(image=image)
272
-
273
-
274
- def update_face_reference(steps: int) -> None:
275
- clear_face_reference()
276
- reference_frame_number = int(preview_slider.get())
277
- roop.globals.reference_face_position += steps
278
- roop.globals.reference_frame_number = reference_frame_number
279
- update_preview(reference_frame_number)
280
-
281
-
282
- def update_frame(steps: int) -> None:
283
- frame_number = preview_slider.get() + steps
284
- preview_slider.set(frame_number)
285
- update_preview(preview_slider.get())
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import webbrowser
4
+ try:
5
+ import customtkinter as ctk
6
+ from tkinterdnd2 import TkinterDnD, DND_ALL
7
+ except ImportError:
8
+ # Headless mode fallback
9
+ class MockTkinterDnD:
10
+ class DnDWrapper: pass
11
+ @staticmethod
12
+ def _require(self): pass
13
+ TkinterDnD = MockTkinterDnD
14
+ DND_ALL = 'DND_ALL'
15
+ ctk = None # Will error if accessed, which is correct for headless
16
+ from typing import Any, Callable, Tuple, Optional
17
+ import cv2
18
+ from PIL import Image, ImageOps
19
+
20
+ import roop.globals
21
+ import roop.metadata
22
+ from roop.face_analyser import get_one_face
23
+ from roop.capturer import get_video_frame, get_video_frame_total
24
+ from roop.face_reference import get_face_reference, set_face_reference, clear_face_reference
25
+ from roop.predictor import predict_frame, clear_predictor
26
+ from roop.processors.frame.core import get_frame_processors_modules
27
+ from roop.utilities import is_image, is_video, resolve_relative_path
28
+
29
+ ROOT = None
30
+ ROOT_HEIGHT = 700
31
+ ROOT_WIDTH = 600
32
+
33
+ PREVIEW = None
34
+ PREVIEW_MAX_HEIGHT = 700
35
+ PREVIEW_MAX_WIDTH = 1200
36
+
37
+ RECENT_DIRECTORY_SOURCE = None
38
+ RECENT_DIRECTORY_TARGET = None
39
+ RECENT_DIRECTORY_OUTPUT = None
40
+
41
+ preview_label = None
42
+ preview_slider = None
43
+ source_label = None
44
+ target_label = None
45
+ status_label = None
46
+
47
+
48
+ # todo: remove by native support -> https://github.com/TomSchimansky/CustomTkinter/issues/934
49
+ class CTk(ctk.CTk, TkinterDnD.DnDWrapper):
50
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
51
+ super().__init__(*args, **kwargs)
52
+ self.TkdndVersion = TkinterDnD._require(self)
53
+
54
+
55
+ def init(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk:
56
+ global ROOT, PREVIEW
57
+
58
+ ROOT = create_root(start, destroy)
59
+ PREVIEW = create_preview(ROOT)
60
+
61
+ return ROOT
62
+
63
+
64
+ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.CTk:
65
+ global source_label, target_label, status_label
66
+
67
+ ctk.deactivate_automatic_dpi_awareness()
68
+ ctk.set_appearance_mode('system')
69
+ ctk.set_default_color_theme(resolve_relative_path('ui.json'))
70
+
71
+ root = CTk()
72
+ root.minsize(ROOT_WIDTH, ROOT_HEIGHT)
73
+ root.title(f'{roop.metadata.name} {roop.metadata.version}')
74
+ root.configure()
75
+ root.protocol('WM_DELETE_WINDOW', lambda: destroy())
76
+
77
+ source_label = ctk.CTkLabel(root, text=None, fg_color=ctk.ThemeManager.theme.get('RoopDropArea').get('fg_color'))
78
+ source_label.place(relx=0.1, rely=0.1, relwidth=0.3, relheight=0.25)
79
+ source_label.drop_target_register(DND_ALL)
80
+ source_label.dnd_bind('<<Drop>>', lambda event: select_source_path(event.data))
81
+ if roop.globals.source_path:
82
+ select_source_path(roop.globals.source_path)
83
+
84
+ target_label = ctk.CTkLabel(root, text=None, fg_color=ctk.ThemeManager.theme.get('RoopDropArea').get('fg_color'))
85
+ target_label.place(relx=0.6, rely=0.1, relwidth=0.3, relheight=0.25)
86
+ target_label.drop_target_register(DND_ALL)
87
+ target_label.dnd_bind('<<Drop>>', lambda event: select_target_path(event.data))
88
+ if roop.globals.target_path:
89
+ select_target_path(roop.globals.target_path)
90
+
91
+ source_button = ctk.CTkButton(root, text='Select a face', cursor='hand2', command=lambda: select_source_path())
92
+ source_button.place(relx=0.1, rely=0.4, relwidth=0.3, relheight=0.1)
93
+
94
+ target_button = ctk.CTkButton(root, text='Select a target', cursor='hand2', command=lambda: select_target_path())
95
+ target_button.place(relx=0.6, rely=0.4, relwidth=0.3, relheight=0.1)
96
+
97
+ keep_fps_value = ctk.BooleanVar(value=roop.globals.keep_fps)
98
+ keep_fps_checkbox = ctk.CTkSwitch(root, text='Keep target fps', variable=keep_fps_value, cursor='hand2', command=lambda: setattr(roop.globals, 'keep_fps', not roop.globals.keep_fps))
99
+ keep_fps_checkbox.place(relx=0.1, rely=0.6)
100
+
101
+ keep_frames_value = ctk.BooleanVar(value=roop.globals.keep_frames)
102
+ keep_frames_switch = ctk.CTkSwitch(root, text='Keep temporary frames', variable=keep_frames_value, cursor='hand2', command=lambda: setattr(roop.globals, 'keep_frames', keep_frames_value.get()))
103
+ keep_frames_switch.place(relx=0.1, rely=0.65)
104
+
105
+ skip_audio_value = ctk.BooleanVar(value=roop.globals.skip_audio)
106
+ skip_audio_switch = ctk.CTkSwitch(root, text='Skip target audio', variable=skip_audio_value, cursor='hand2', command=lambda: setattr(roop.globals, 'skip_audio', skip_audio_value.get()))
107
+ skip_audio_switch.place(relx=0.6, rely=0.6)
108
+
109
+ many_faces_value = ctk.BooleanVar(value=roop.globals.many_faces)
110
+ many_faces_switch = ctk.CTkSwitch(root, text='Many faces', variable=many_faces_value, cursor='hand2', command=lambda: setattr(roop.globals, 'many_faces', many_faces_value.get()))
111
+ many_faces_switch.place(relx=0.6, rely=0.65)
112
+
113
+ start_button = ctk.CTkButton(root, text='Start', cursor='hand2', command=lambda: select_output_path(start))
114
+ start_button.place(relx=0.15, rely=0.75, relwidth=0.2, relheight=0.05)
115
+
116
+ stop_button = ctk.CTkButton(root, text='Destroy', cursor='hand2', command=lambda: destroy())
117
+ stop_button.place(relx=0.4, rely=0.75, relwidth=0.2, relheight=0.05)
118
+
119
+ preview_button = ctk.CTkButton(root, text='Preview', cursor='hand2', command=lambda: toggle_preview())
120
+ preview_button.place(relx=0.65, rely=0.75, relwidth=0.2, relheight=0.05)
121
+
122
+ status_label = ctk.CTkLabel(root, text=None, justify='center')
123
+ status_label.place(relx=0.1, rely=0.9, relwidth=0.8)
124
+
125
+ donate_label = ctk.CTkLabel(root, text='^_^ Donate to project ^_^', justify='center', cursor='hand2')
126
+ donate_label.place(relx=0.1, rely=0.95, relwidth=0.8)
127
+ donate_label.configure(text_color=ctk.ThemeManager.theme.get('RoopDonate').get('text_color'))
128
+ donate_label.bind('<Button>', lambda event: webbrowser.open('https://github.com/sponsors/s0md3v'))
129
+
130
+ return root
131
+
132
+
133
+ def create_preview(parent: ctk.CTkToplevel) -> ctk.CTkToplevel:
134
+ global preview_label, preview_slider
135
+
136
+ preview = ctk.CTkToplevel(parent)
137
+ preview.withdraw()
138
+ preview.configure()
139
+ preview.protocol('WM_DELETE_WINDOW', lambda: toggle_preview())
140
+ preview.resizable(width=False, height=False)
141
+
142
+ preview_label = ctk.CTkLabel(preview, text=None)
143
+ preview_label.pack(fill='both', expand=True)
144
+
145
+ preview_slider = ctk.CTkSlider(preview, from_=0, to=0, command=lambda frame_value: update_preview(frame_value))
146
+
147
+ preview.bind('<Up>', lambda event: update_face_reference(1))
148
+ preview.bind('<Down>', lambda event: update_face_reference(-1))
149
+ return preview
150
+
151
+
152
+ def update_status(text: str) -> None:
153
+ status_label.configure(text=text)
154
+ ROOT.update()
155
+
156
+
157
+ def select_source_path(source_path: Optional[str] = None) -> None:
158
+ global RECENT_DIRECTORY_SOURCE
159
+
160
+ if PREVIEW:
161
+ PREVIEW.withdraw()
162
+ if source_path is None:
163
+ source_path = ctk.filedialog.askopenfilename(title='select an source image', initialdir=RECENT_DIRECTORY_SOURCE)
164
+ if is_image(source_path):
165
+ roop.globals.source_path = source_path
166
+ RECENT_DIRECTORY_SOURCE = os.path.dirname(roop.globals.source_path)
167
+ image = render_image_preview(roop.globals.source_path, (200, 200))
168
+ source_label.configure(image=image)
169
+ else:
170
+ roop.globals.source_path = None
171
+ source_label.configure(image=None)
172
+
173
+
174
+ def select_target_path(target_path: Optional[str] = None) -> None:
175
+ global RECENT_DIRECTORY_TARGET
176
+
177
+ if PREVIEW:
178
+ PREVIEW.withdraw()
179
+ clear_face_reference()
180
+ if target_path is None:
181
+ target_path = ctk.filedialog.askopenfilename(title='select an target image or video', initialdir=RECENT_DIRECTORY_TARGET)
182
+ if is_image(target_path):
183
+ roop.globals.target_path = target_path
184
+ RECENT_DIRECTORY_TARGET = os.path.dirname(roop.globals.target_path)
185
+ image = render_image_preview(roop.globals.target_path, (200, 200))
186
+ target_label.configure(image=image)
187
+ elif is_video(target_path):
188
+ roop.globals.target_path = target_path
189
+ RECENT_DIRECTORY_TARGET = os.path.dirname(roop.globals.target_path)
190
+ video_frame = render_video_preview(target_path, (200, 200))
191
+ target_label.configure(image=video_frame)
192
+ else:
193
+ roop.globals.target_path = None
194
+ target_label.configure(image=None)
195
+
196
+
197
+ def select_output_path(start: Callable[[], None]) -> None:
198
+ global RECENT_DIRECTORY_OUTPUT
199
+
200
+ if is_image(roop.globals.target_path):
201
+ output_path = ctk.filedialog.asksaveasfilename(title='save image output file', defaultextension='.png', initialfile='output.png', initialdir=RECENT_DIRECTORY_OUTPUT)
202
+ elif is_video(roop.globals.target_path):
203
+ output_path = ctk.filedialog.asksaveasfilename(title='save video output file', defaultextension='.mp4', initialfile='output.mp4', initialdir=RECENT_DIRECTORY_OUTPUT)
204
+ else:
205
+ output_path = None
206
+ if output_path:
207
+ roop.globals.output_path = output_path
208
+ RECENT_DIRECTORY_OUTPUT = os.path.dirname(roop.globals.output_path)
209
+ start()
210
+
211
+
212
+ def render_image_preview(image_path: str, size: Tuple[int, int]) -> ctk.CTkImage:
213
+ image = Image.open(image_path)
214
+ if size:
215
+ image = ImageOps.fit(image, size, Image.LANCZOS)
216
+ return ctk.CTkImage(image, size=image.size)
217
+
218
+
219
+ def render_video_preview(video_path: str, size: Tuple[int, int], frame_number: int = 0) -> ctk.CTkImage:
220
+ capture = cv2.VideoCapture(video_path)
221
+ if frame_number:
222
+ capture.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
223
+ has_frame, frame = capture.read()
224
+ if has_frame:
225
+ image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
226
+ if size:
227
+ image = ImageOps.fit(image, size, Image.LANCZOS)
228
+ return ctk.CTkImage(image, size=image.size)
229
+ capture.release()
230
+ cv2.destroyAllWindows()
231
+
232
+
233
+ def toggle_preview() -> None:
234
+ if PREVIEW.state() == 'normal':
235
+ PREVIEW.unbind('<Right>')
236
+ PREVIEW.unbind('<Left>')
237
+ PREVIEW.withdraw()
238
+ clear_predictor()
239
+ elif roop.globals.source_path and roop.globals.target_path:
240
+ init_preview()
241
+ update_preview(roop.globals.reference_frame_number)
242
+ PREVIEW.deiconify()
243
+
244
+
245
+ def init_preview() -> None:
246
+ PREVIEW.title('Preview [ ↕ Reference face ]')
247
+ if is_image(roop.globals.target_path):
248
+ preview_slider.pack_forget()
249
+ if is_video(roop.globals.target_path):
250
+ video_frame_total = get_video_frame_total(roop.globals.target_path)
251
+ if video_frame_total > 0:
252
+ PREVIEW.title('Preview [ Reference face ] [ ↔ Frame number ]')
253
+ PREVIEW.bind('<Right>', lambda event: update_frame(int(video_frame_total / 20)))
254
+ PREVIEW.bind('<Left>', lambda event: update_frame(int(video_frame_total / -20)))
255
+ preview_slider.configure(to=video_frame_total)
256
+ preview_slider.pack(fill='x')
257
+ preview_slider.set(roop.globals.reference_frame_number)
258
+
259
+
260
+ def update_preview(frame_number: int = 0) -> None:
261
+ if roop.globals.source_path and roop.globals.target_path:
262
+ temp_frame = get_video_frame(roop.globals.target_path, frame_number)
263
+ if predict_frame(temp_frame):
264
+ sys.exit()
265
+ source_face = get_one_face(cv2.imread(roop.globals.source_path))
266
+ if not get_face_reference():
267
+ reference_frame = get_video_frame(roop.globals.target_path, roop.globals.reference_frame_number)
268
+ reference_face = get_one_face(reference_frame, roop.globals.reference_face_position)
269
+ set_face_reference(reference_face)
270
+ else:
271
+ reference_face = get_face_reference()
272
+ for frame_processor in get_frame_processors_modules(roop.globals.frame_processors):
273
+ temp_frame = frame_processor.process_frame(
274
+ source_face,
275
+ reference_face,
276
+ temp_frame
277
+ )
278
+ image = Image.fromarray(cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB))
279
+ image = ImageOps.contain(image, (PREVIEW_MAX_WIDTH, PREVIEW_MAX_HEIGHT), Image.LANCZOS)
280
+ image = ctk.CTkImage(image, size=image.size)
281
+ preview_label.configure(image=image)
282
+
283
+
284
+ def update_face_reference(steps: int) -> None:
285
+ clear_face_reference()
286
+ reference_frame_number = int(preview_slider.get())
287
+ roop.globals.reference_face_position += steps
288
+ roop.globals.reference_frame_number = reference_frame_number
289
+ update_preview(reference_frame_number)
290
+
291
+
292
+ def update_frame(steps: int) -> None:
293
+ frame_number = preview_slider.get() + steps
294
+ preview_slider.set(frame_number)
295
+ update_preview(preview_slider.get())