mirror of
https://github.com/hacksider/Deep-Live-Cam.git
synced 2025-03-17 21:31:51 +01:00
Eyebrow Mask
This commit is contained in:
parent
5dfd1c0ced
commit
d8fc1ffa04
@ -43,5 +43,7 @@ mask_down_size = 0.50
|
||||
mask_size = 1
|
||||
eyes_mask = False
|
||||
show_eyes_mask_box = False
|
||||
eyebrows_mask = False
|
||||
show_eyebrows_mask_box = False
|
||||
use_fake_face = False
|
||||
fake_face_path = None
|
||||
|
@ -111,6 +111,23 @@ def swap_face(source_face: Face, target_face: Face, temp_frame: Frame) -> Frame:
|
||||
swapped_frame, target_face, eyes_mask_data
|
||||
)
|
||||
|
||||
if modules.globals.eyebrows_mask:
|
||||
# Create the eyebrows mask
|
||||
eyebrows_mask, eyebrows_cutout, eyebrows_box, eyebrows_polygon = (
|
||||
create_eyebrows_mask(target_face, temp_frame)
|
||||
)
|
||||
|
||||
# Apply the eyebrows area
|
||||
swapped_frame = apply_eyebrows_area(
|
||||
swapped_frame, eyebrows_cutout, eyebrows_box, face_mask, eyebrows_polygon
|
||||
)
|
||||
|
||||
if modules.globals.show_eyebrows_mask_box:
|
||||
eyebrows_mask_data = (eyebrows_mask, eyebrows_cutout, eyebrows_box, eyebrows_polygon)
|
||||
swapped_frame = draw_eyebrows_mask_visualization(
|
||||
swapped_frame, target_face, eyebrows_mask_data
|
||||
)
|
||||
|
||||
return swapped_frame
|
||||
|
||||
|
||||
@ -859,3 +876,264 @@ def draw_eyes_mask_visualization(
|
||||
|
||||
return vis_frame
|
||||
return frame
|
||||
|
||||
|
||||
def create_eyebrows_mask(face: Face, frame: Frame) -> (np.ndarray, np.ndarray, tuple, np.ndarray):
|
||||
mask = np.zeros(frame.shape[:2], dtype=np.uint8)
|
||||
eyebrows_cutout = None
|
||||
landmarks = face.landmark_2d_106
|
||||
if landmarks is not None:
|
||||
# Left eyebrow landmarks (97-105) and right eyebrow landmarks (43-51)
|
||||
left_eyebrow = landmarks[97:105].astype(np.float32)
|
||||
right_eyebrow = landmarks[43:51].astype(np.float32)
|
||||
|
||||
# Calculate centers and dimensions for each eyebrow
|
||||
left_center = np.mean(left_eyebrow, axis=0)
|
||||
right_center = np.mean(right_eyebrow, axis=0)
|
||||
|
||||
# Calculate bounding box with padding
|
||||
all_points = np.vstack([left_eyebrow, right_eyebrow])
|
||||
min_x = np.min(all_points[:, 0]) - 25
|
||||
max_x = np.max(all_points[:, 0]) + 25
|
||||
min_y = np.min(all_points[:, 1]) - 20
|
||||
max_y = np.max(all_points[:, 1]) + 15
|
||||
|
||||
# Ensure coordinates are within frame bounds
|
||||
min_x = max(0, int(min_x))
|
||||
min_y = max(0, int(min_y))
|
||||
max_x = min(frame.shape[1], int(max_x))
|
||||
max_y = min(frame.shape[0], int(max_y))
|
||||
|
||||
# Create mask for the eyebrows region
|
||||
mask_roi = np.zeros((max_y - min_y, max_x - min_x), dtype=np.uint8)
|
||||
|
||||
try:
|
||||
# Convert points to local coordinates
|
||||
left_local = left_eyebrow - [min_x, min_y]
|
||||
right_local = right_eyebrow - [min_x, min_y]
|
||||
|
||||
def create_curved_eyebrow(points):
|
||||
if len(points) >= 5:
|
||||
# Sort points by x-coordinate
|
||||
sorted_idx = np.argsort(points[:, 0])
|
||||
sorted_points = points[sorted_idx]
|
||||
|
||||
# Calculate dimensions
|
||||
x_min, y_min = np.min(sorted_points, axis=0)
|
||||
x_max, y_max = np.max(sorted_points, axis=0)
|
||||
width = x_max - x_min
|
||||
height = y_max - y_min
|
||||
|
||||
# Create more points for smoother curve
|
||||
num_points = 50
|
||||
x = np.linspace(x_min, x_max, num_points)
|
||||
|
||||
# Fit cubic curve through points for more natural arch
|
||||
coeffs = np.polyfit(sorted_points[:, 0], sorted_points[:, 1], 3)
|
||||
y = np.polyval(coeffs, x)
|
||||
|
||||
# Create points for top and bottom curves with varying offsets
|
||||
top_offset = np.linspace(height * 0.4, height * 0.3, num_points) # Varying offset for more natural shape
|
||||
bottom_offset = np.linspace(height * 0.2, height * 0.15, num_points)
|
||||
|
||||
# Add some randomness to the offsets for more natural look
|
||||
top_offset += np.random.normal(0, height * 0.02, num_points)
|
||||
bottom_offset += np.random.normal(0, height * 0.01, num_points)
|
||||
|
||||
# Smooth the offsets
|
||||
top_offset = cv2.GaussianBlur(top_offset.reshape(-1, 1), (1, 3), 1).reshape(-1)
|
||||
bottom_offset = cv2.GaussianBlur(bottom_offset.reshape(-1, 1), (1, 3), 1).reshape(-1)
|
||||
|
||||
top_curve = y - top_offset
|
||||
bottom_curve = y + bottom_offset
|
||||
|
||||
# Create curved endpoints
|
||||
end_points = 5
|
||||
start_curve = np.column_stack((
|
||||
np.linspace(x[0] - width * 0.05, x[0], end_points),
|
||||
np.linspace(bottom_curve[0], top_curve[0], end_points)
|
||||
))
|
||||
end_curve = np.column_stack((
|
||||
np.linspace(x[-1], x[-1] + width * 0.05, end_points),
|
||||
np.linspace(bottom_curve[-1], top_curve[-1], end_points)
|
||||
))
|
||||
|
||||
# Combine all points to form a smooth contour
|
||||
contour_points = np.vstack([
|
||||
start_curve,
|
||||
np.column_stack((x, top_curve)),
|
||||
end_curve,
|
||||
np.column_stack((x[::-1], bottom_curve[::-1]))
|
||||
])
|
||||
|
||||
# Add padding and smooth the shape
|
||||
center = np.mean(contour_points, axis=0)
|
||||
vectors = contour_points - center
|
||||
padded_points = center + vectors * 1.2 # 20% padding
|
||||
|
||||
# Convert to integer coordinates and draw
|
||||
cv2.fillPoly(mask_roi, [padded_points.astype(np.int32)], 255)
|
||||
|
||||
return padded_points
|
||||
return points
|
||||
|
||||
# Generate and draw eyebrow shapes
|
||||
left_shape = create_curved_eyebrow(left_local)
|
||||
right_shape = create_curved_eyebrow(right_local)
|
||||
|
||||
# Apply multi-stage blurring for natural feathering
|
||||
# First, strong Gaussian blur for initial softening
|
||||
mask_roi = cv2.GaussianBlur(mask_roi, (21, 21), 7)
|
||||
|
||||
# Second, medium blur for transition areas
|
||||
mask_roi = cv2.GaussianBlur(mask_roi, (11, 11), 3)
|
||||
|
||||
# Finally, light blur for fine details
|
||||
mask_roi = cv2.GaussianBlur(mask_roi, (5, 5), 1)
|
||||
|
||||
# Normalize mask values
|
||||
mask_roi = cv2.normalize(mask_roi, None, 0, 255, cv2.NORM_MINMAX)
|
||||
|
||||
# Place the mask ROI in the full-sized mask
|
||||
mask[min_y:max_y, min_x:max_x] = mask_roi
|
||||
|
||||
# Extract the masked area from the frame
|
||||
eyebrows_cutout = frame[min_y:max_y, min_x:max_x].copy()
|
||||
|
||||
# Combine points for visualization
|
||||
eyebrows_polygon = np.vstack([
|
||||
left_shape + [min_x, min_y],
|
||||
right_shape + [min_x, min_y]
|
||||
]).astype(np.int32)
|
||||
|
||||
except Exception as e:
|
||||
# Fallback to simple polygons if curve fitting fails
|
||||
left_local = left_eyebrow - [min_x, min_y]
|
||||
right_local = right_eyebrow - [min_x, min_y]
|
||||
cv2.fillPoly(mask_roi, [left_local.astype(np.int32)], 255)
|
||||
cv2.fillPoly(mask_roi, [right_local.astype(np.int32)], 255)
|
||||
mask_roi = cv2.GaussianBlur(mask_roi, (21, 21), 7)
|
||||
mask[min_y:max_y, min_x:max_x] = mask_roi
|
||||
eyebrows_cutout = frame[min_y:max_y, min_x:max_x].copy()
|
||||
eyebrows_polygon = np.vstack([left_eyebrow, right_eyebrow]).astype(np.int32)
|
||||
|
||||
return mask, eyebrows_cutout, (min_x, min_y, max_x, max_y), eyebrows_polygon
|
||||
|
||||
|
||||
def apply_eyebrows_area(
|
||||
frame: np.ndarray,
|
||||
eyebrows_cutout: np.ndarray,
|
||||
eyebrows_box: tuple,
|
||||
face_mask: np.ndarray,
|
||||
eyebrows_polygon: np.ndarray,
|
||||
) -> np.ndarray:
|
||||
min_x, min_y, max_x, max_y = eyebrows_box
|
||||
box_width = max_x - min_x
|
||||
box_height = max_y - min_y
|
||||
|
||||
if (
|
||||
eyebrows_cutout is None
|
||||
or box_width is None
|
||||
or box_height is None
|
||||
or face_mask is None
|
||||
or eyebrows_polygon is None
|
||||
):
|
||||
return frame
|
||||
|
||||
try:
|
||||
resized_eyebrows_cutout = cv2.resize(eyebrows_cutout, (box_width, box_height))
|
||||
roi = frame[min_y:max_y, min_x:max_x]
|
||||
|
||||
if roi.shape != resized_eyebrows_cutout.shape:
|
||||
resized_eyebrows_cutout = cv2.resize(
|
||||
resized_eyebrows_cutout, (roi.shape[1], roi.shape[0])
|
||||
)
|
||||
|
||||
color_corrected_eyebrows = apply_color_transfer(resized_eyebrows_cutout, roi)
|
||||
|
||||
# Create mask for both eyebrows
|
||||
polygon_mask = np.zeros(roi.shape[:2], dtype=np.uint8)
|
||||
|
||||
# Split points for left and right eyebrows
|
||||
mid_point = len(eyebrows_polygon) // 2
|
||||
left_points = eyebrows_polygon[:mid_point] - [min_x, min_y]
|
||||
right_points = eyebrows_polygon[mid_point:] - [min_x, min_y]
|
||||
|
||||
# Draw filled polygons
|
||||
cv2.fillPoly(polygon_mask, [left_points], 255)
|
||||
cv2.fillPoly(polygon_mask, [right_points], 255)
|
||||
|
||||
# Apply strong initial feathering
|
||||
polygon_mask = cv2.GaussianBlur(polygon_mask, (21, 21), 7)
|
||||
|
||||
# Apply additional feathering
|
||||
feather_amount = min(
|
||||
30,
|
||||
box_width // modules.globals.mask_feather_ratio,
|
||||
box_height // modules.globals.mask_feather_ratio,
|
||||
)
|
||||
feathered_mask = cv2.GaussianBlur(
|
||||
polygon_mask.astype(float), (0, 0), feather_amount
|
||||
)
|
||||
feathered_mask = feathered_mask / feathered_mask.max()
|
||||
|
||||
# Apply additional smoothing to the mask edges
|
||||
feathered_mask = cv2.GaussianBlur(feathered_mask, (5, 5), 1)
|
||||
|
||||
face_mask_roi = face_mask[min_y:max_y, min_x:max_x]
|
||||
combined_mask = feathered_mask * (face_mask_roi / 255.0)
|
||||
|
||||
combined_mask = combined_mask[:, :, np.newaxis]
|
||||
blended = (
|
||||
color_corrected_eyebrows * combined_mask + roi * (1 - combined_mask)
|
||||
).astype(np.uint8)
|
||||
|
||||
# Apply face mask to blended result
|
||||
face_mask_3channel = (
|
||||
np.repeat(face_mask_roi[:, :, np.newaxis], 3, axis=2) / 255.0
|
||||
)
|
||||
final_blend = blended * face_mask_3channel + roi * (1 - face_mask_3channel)
|
||||
|
||||
frame[min_y:max_y, min_x:max_x] = final_blend.astype(np.uint8)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
return frame
|
||||
|
||||
|
||||
def draw_eyebrows_mask_visualization(
|
||||
frame: Frame, face: Face, eyebrows_mask_data: tuple
|
||||
) -> Frame:
|
||||
landmarks = face.landmark_2d_106
|
||||
if landmarks is not None and eyebrows_mask_data is not None:
|
||||
mask, eyebrows_cutout, (min_x, min_y, max_x, max_y), eyebrows_polygon = eyebrows_mask_data
|
||||
|
||||
vis_frame = frame.copy()
|
||||
|
||||
# Ensure coordinates are within frame bounds
|
||||
height, width = vis_frame.shape[:2]
|
||||
min_x, min_y = max(0, min_x), max(0, min_y)
|
||||
max_x, max_y = min(width, max_x), min(height, max_y)
|
||||
|
||||
# Draw the eyebrows curves
|
||||
mid_point = len(eyebrows_polygon) // 2
|
||||
left_points = eyebrows_polygon[:mid_point]
|
||||
right_points = eyebrows_polygon[mid_point:]
|
||||
|
||||
# Draw smooth curves with anti-aliasing
|
||||
cv2.polylines(vis_frame, [left_points], True, (0, 255, 0), 2, cv2.LINE_AA)
|
||||
cv2.polylines(vis_frame, [right_points], True, (0, 255, 0), 2, cv2.LINE_AA)
|
||||
|
||||
# Add label
|
||||
cv2.putText(
|
||||
vis_frame,
|
||||
"Eyebrows Mask",
|
||||
(min_x, min_y - 10),
|
||||
cv2.FONT_HERSHEY_SIMPLEX,
|
||||
0.5,
|
||||
(255, 255, 255),
|
||||
1,
|
||||
)
|
||||
|
||||
return vis_frame
|
||||
return frame
|
||||
|
@ -36,7 +36,7 @@ if platform.system() == "Windows":
|
||||
ROOT = None
|
||||
POPUP = None
|
||||
POPUP_LIVE = None
|
||||
ROOT_HEIGHT = 700
|
||||
ROOT_HEIGHT = 730
|
||||
ROOT_WIDTH = 600
|
||||
|
||||
PREVIEW = None
|
||||
@ -167,20 +167,20 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
|
||||
# Image Selection Area (Top)
|
||||
source_label = ctk.CTkLabel(root, text=None)
|
||||
source_label.place(relx=0.1, rely=0.1, relwidth=0.3, relheight=0.25)
|
||||
source_label.place(relx=0.1, rely=0.05, relwidth=0.3, relheight=0.25)
|
||||
|
||||
target_label = ctk.CTkLabel(root, text=None)
|
||||
target_label.place(relx=0.6, rely=0.1, relwidth=0.3, relheight=0.25)
|
||||
target_label.place(relx=0.6, rely=0.05, 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.place(relx=0.1, rely=0.4, relwidth=0.3, relheight=0.1)
|
||||
select_face_button.place(relx=0.1, rely=0.35, relwidth=0.3, relheight=0.1)
|
||||
|
||||
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)
|
||||
swap_faces_button.place(relx=0.45, rely=0.35, relwidth=0.1, relheight=0.1)
|
||||
|
||||
select_target_button = ctk.CTkButton(
|
||||
root,
|
||||
@ -188,7 +188,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
cursor="hand2",
|
||||
command=lambda: 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.35, relwidth=0.3, relheight=0.1)
|
||||
|
||||
# AI Generated Face controls
|
||||
fake_face_value = ctk.BooleanVar(value=modules.globals.use_fake_face)
|
||||
@ -199,7 +199,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
cursor="hand2",
|
||||
command=lambda: toggle_fake_face(fake_face_value)
|
||||
)
|
||||
fake_face_switch.place(relx=0.1, rely=0.55)
|
||||
fake_face_switch.place(relx=0.1, rely=0.50)
|
||||
|
||||
# Add refresh button next to the switch
|
||||
refresh_face_button = ctk.CTkButton(
|
||||
@ -209,7 +209,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
cursor="hand2",
|
||||
command=lambda: refresh_fake_face_clicked()
|
||||
)
|
||||
refresh_face_button.place(relx=0.35, rely=0.55)
|
||||
refresh_face_button.place(relx=0.35, rely=0.50)
|
||||
|
||||
# Face Processing Options (Middle Left)
|
||||
many_faces_value = ctk.BooleanVar(value=modules.globals.many_faces)
|
||||
@ -223,7 +223,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
save_switch_states(),
|
||||
),
|
||||
)
|
||||
many_faces_switch.place(relx=0.1, rely=0.60)
|
||||
many_faces_switch.place(relx=0.1, rely=0.55)
|
||||
|
||||
map_faces = ctk.BooleanVar(value=modules.globals.map_faces)
|
||||
map_faces_switch = ctk.CTkSwitch(
|
||||
@ -237,7 +237,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
close_mapper_window() if not map_faces.get() else None
|
||||
),
|
||||
)
|
||||
map_faces_switch.place(relx=0.1, rely=0.65)
|
||||
map_faces_switch.place(relx=0.1, rely=0.60)
|
||||
|
||||
enhancer_value = ctk.BooleanVar(value=modules.globals.fp_ui["face_enhancer"])
|
||||
enhancer_switch = ctk.CTkSwitch(
|
||||
@ -250,7 +250,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
save_switch_states(),
|
||||
),
|
||||
)
|
||||
enhancer_switch.place(relx=0.1, rely=0.70)
|
||||
enhancer_switch.place(relx=0.1, rely=0.65)
|
||||
|
||||
keep_audio_value = ctk.BooleanVar(value=modules.globals.keep_audio)
|
||||
keep_audio_switch = ctk.CTkSwitch(
|
||||
@ -263,7 +263,21 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
save_switch_states(),
|
||||
),
|
||||
)
|
||||
keep_audio_switch.place(relx=0.1, rely=0.75)
|
||||
keep_audio_switch.place(relx=0.1, rely=0.70)
|
||||
|
||||
# Add show FPS switch right after keep_audio_switch
|
||||
show_fps_value = ctk.BooleanVar(value=modules.globals.show_fps)
|
||||
show_fps_switch = ctk.CTkSwitch(
|
||||
root,
|
||||
text=_("Show FPS"),
|
||||
variable=show_fps_value,
|
||||
cursor="hand2",
|
||||
command=lambda: (
|
||||
setattr(modules.globals, "show_fps", show_fps_value.get()),
|
||||
save_switch_states(),
|
||||
),
|
||||
)
|
||||
show_fps_switch.place(relx=0.1, rely=0.75)
|
||||
|
||||
# Additional Options (Middle Right)
|
||||
mouth_mask_var = ctk.BooleanVar(value=modules.globals.mouth_mask)
|
||||
@ -274,7 +288,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
cursor="hand2",
|
||||
command=lambda: setattr(modules.globals, "mouth_mask", mouth_mask_var.get()),
|
||||
)
|
||||
mouth_mask_switch.place(relx=0.6, rely=0.55)
|
||||
mouth_mask_switch.place(relx=0.6, rely=0.50)
|
||||
|
||||
show_mouth_mask_box_var = ctk.BooleanVar(value=modules.globals.show_mouth_mask_box)
|
||||
show_mouth_mask_box_switch = ctk.CTkSwitch(
|
||||
@ -286,7 +300,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
modules.globals, "show_mouth_mask_box", show_mouth_mask_box_var.get()
|
||||
),
|
||||
)
|
||||
show_mouth_mask_box_switch.place(relx=0.6, rely=0.60)
|
||||
show_mouth_mask_box_switch.place(relx=0.6, rely=0.55)
|
||||
|
||||
# Add eyes mask switch
|
||||
eyes_mask_var = ctk.BooleanVar(value=modules.globals.eyes_mask)
|
||||
@ -297,7 +311,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
cursor="hand2",
|
||||
command=lambda: setattr(modules.globals, "eyes_mask", eyes_mask_var.get()),
|
||||
)
|
||||
eyes_mask_switch.place(relx=0.6, rely=0.65)
|
||||
eyes_mask_switch.place(relx=0.6, rely=0.60)
|
||||
|
||||
# Add show eyes mask box switch
|
||||
show_eyes_mask_box_var = ctk.BooleanVar(value=modules.globals.show_eyes_mask_box)
|
||||
@ -310,21 +324,30 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
modules.globals, "show_eyes_mask_box", show_eyes_mask_box_var.get()
|
||||
),
|
||||
)
|
||||
show_eyes_mask_box_switch.place(relx=0.6, rely=0.70)
|
||||
show_eyes_mask_box_switch.place(relx=0.6, rely=0.65)
|
||||
|
||||
# Add show FPS switch
|
||||
show_fps_value = ctk.BooleanVar(value=modules.globals.show_fps)
|
||||
show_fps_switch = ctk.CTkSwitch(
|
||||
# Move the eyebrows mask switches up slightly
|
||||
eyebrows_mask_var = ctk.BooleanVar(value=modules.globals.eyebrows_mask)
|
||||
eyebrows_mask_switch = ctk.CTkSwitch(
|
||||
root,
|
||||
text=_("Show FPS"),
|
||||
variable=show_fps_value,
|
||||
text=_("Eyebrows Mask"),
|
||||
variable=eyebrows_mask_var,
|
||||
cursor="hand2",
|
||||
command=lambda: (
|
||||
setattr(modules.globals, "show_fps", show_fps_value.get()),
|
||||
save_switch_states(),
|
||||
command=lambda: setattr(modules.globals, "eyebrows_mask", eyebrows_mask_var.get()),
|
||||
)
|
||||
eyebrows_mask_switch.place(relx=0.6, rely=0.70)
|
||||
|
||||
show_eyebrows_mask_box_var = ctk.BooleanVar(value=modules.globals.show_eyebrows_mask_box)
|
||||
show_eyebrows_mask_box_switch = ctk.CTkSwitch(
|
||||
root,
|
||||
text=_("Show Eyebrows Mask Box"),
|
||||
variable=show_eyebrows_mask_box_var,
|
||||
cursor="hand2",
|
||||
command=lambda: setattr(
|
||||
modules.globals, "show_eyebrows_mask_box", show_eyebrows_mask_box_var.get()
|
||||
),
|
||||
)
|
||||
show_fps_switch.place(relx=0.6, rely=0.75)
|
||||
show_eyebrows_mask_box_switch.place(relx=0.6, rely=0.75)
|
||||
|
||||
# Main Control Buttons (Bottom)
|
||||
start_button = ctk.CTkButton(
|
||||
@ -392,7 +415,7 @@ def create_root(start: Callable[[], None], destroy: Callable[[], None]) -> ctk.C
|
||||
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.place(relx=0.1, rely=0.94, relwidth=0.8)
|
||||
donate_label.configure(
|
||||
text_color=ctk.ThemeManager.theme.get("URL").get("text_color")
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user