Eyebrow Mask
							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…
	
		Reference in New Issue