Fix: Prevent immediate stop/crash in live preview if source image is invalid.
- Modified `create_webcam_preview` in `modules/ui.py` to handle errors during source image loading (e.g., file not readable, no face detected) more gracefully. - Instead of stopping the live preview, it now shows a warning message and starts the live camera feed without face swapping if the source image is unusable. - This addresses the issue where clicking 'Live' after selecting a problematic source image would cause the preview to stop or crash immediately.pull/1380/head
parent
eb33df659b
commit
d00af5a995
164
modules/ui.py
164
modules/ui.py
|
@ -880,84 +880,122 @@ def create_webcam_preview(camera_index: int):
|
||||||
PREVIEW.deiconify()
|
PREVIEW.deiconify()
|
||||||
|
|
||||||
frame_processors = get_frame_processors_modules(modules.globals.frame_processors)
|
frame_processors = get_frame_processors_modules(modules.globals.frame_processors)
|
||||||
|
# Get initial source image if not mapping faces
|
||||||
source_image = None
|
source_image = None
|
||||||
prev_time = time.time()
|
if not modules.globals.map_faces and modules.globals.source_path:
|
||||||
fps_update_interval = 0.5
|
try:
|
||||||
frame_count = 0
|
loaded_cv_image = cv2.imread(modules.globals.source_path)
|
||||||
fps = 0
|
if loaded_cv_image is None:
|
||||||
|
update_status(f"Error: Could not read source image at {modules.globals.source_path}")
|
||||||
|
# source_image remains None
|
||||||
|
else:
|
||||||
|
source_image = get_one_face(loaded_cv_image)
|
||||||
|
if source_image is None:
|
||||||
|
update_status(f"Error: No face detected in source image {os.path.basename(modules.globals.source_path)}")
|
||||||
|
except Exception as e:
|
||||||
|
update_status(f"Exception loading source image: {str(e)[:100]}")
|
||||||
|
source_image = None # Ensure source_image is None on any error
|
||||||
|
|
||||||
while True:
|
# If source_image is still None AND a source_path was provided (meaning user intended a swap)
|
||||||
ret, frame = cap.read()
|
# AND we are not using map_faces (which handles its own source logic for sources)
|
||||||
if not ret:
|
if source_image is None and modules.globals.source_path and not modules.globals.map_faces:
|
||||||
break
|
update_status("Warning: Live preview started, but source image is invalid or has no face. No swap will occur.")
|
||||||
|
# The live preview will start, but no swap will occur if source_image is None.
|
||||||
|
|
||||||
temp_frame = frame.copy()
|
# Start the update loop
|
||||||
|
fps_data = { # Moved fps_data initialization here to be passed to the loop
|
||||||
|
"prev_time": time.time(),
|
||||||
|
"frame_count": 0,
|
||||||
|
"fps": 0.0,
|
||||||
|
"fps_update_interval": 0.5
|
||||||
|
}
|
||||||
|
update_webcam_frame_after(cap, frame_processors, source_image, fps_data)
|
||||||
|
|
||||||
if modules.globals.live_mirror:
|
|
||||||
temp_frame = cv2.flip(temp_frame, 1)
|
|
||||||
|
|
||||||
if modules.globals.live_resizable:
|
def update_webcam_frame_after(cap, frame_processors, source_image, fps_data, delay_ms=15): # Approx 66 FPS target for UI updates
|
||||||
temp_frame = fit_image_to_size(
|
global preview_label, ROOT, PREVIEW
|
||||||
temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height()
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
if PREVIEW.state() == "withdrawn":
|
||||||
temp_frame = fit_image_to_size(
|
cap.release()
|
||||||
temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height()
|
PREVIEW.withdraw() # Ensure it's withdrawn if loop exits
|
||||||
)
|
return
|
||||||
|
|
||||||
if not modules.globals.map_faces:
|
ret, frame = cap.read()
|
||||||
if source_image is None and modules.globals.source_path:
|
if not ret:
|
||||||
source_image = get_one_face(cv2.imread(modules.globals.source_path))
|
# Handle camera read failure or end of stream (though for webcam, it's usually continuous)
|
||||||
|
ROOT.after(delay_ms, lambda: update_webcam_frame_after(cap, frame_processors, source_image, fps_data, delay_ms))
|
||||||
|
return
|
||||||
|
|
||||||
for frame_processor in frame_processors:
|
temp_frame = frame.copy()
|
||||||
if frame_processor.NAME == "DLC.FACE-ENHANCER":
|
|
||||||
if modules.globals.fp_ui["face_enhancer"]:
|
if modules.globals.live_mirror:
|
||||||
temp_frame = frame_processor.process_frame(None, temp_frame)
|
temp_frame = cv2.flip(temp_frame, 1)
|
||||||
else:
|
|
||||||
temp_frame = frame_processor.process_frame(source_image, temp_frame)
|
# Resizing based on PREVIEW window dimensions.
|
||||||
else:
|
preview_width = PREVIEW.winfo_width()
|
||||||
modules.globals.target_path = None
|
preview_height = PREVIEW.winfo_height()
|
||||||
for frame_processor in frame_processors:
|
if preview_width > 1 and preview_height > 1: # Ensure valid dimensions
|
||||||
if frame_processor.NAME == "DLC.FACE-ENHANCER":
|
temp_frame = fit_image_to_size(temp_frame, preview_width, preview_height)
|
||||||
if modules.globals.fp_ui["face_enhancer"]:
|
|
||||||
temp_frame = frame_processor.process_frame_v2(temp_frame)
|
|
||||||
else:
|
if not modules.globals.map_faces:
|
||||||
|
current_source_image = source_image
|
||||||
|
if current_source_image is None and modules.globals.source_path:
|
||||||
|
try:
|
||||||
|
current_source_image = get_one_face(cv2.imread(modules.globals.source_path))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
for frame_processor in frame_processors:
|
||||||
|
if frame_processor.NAME == "DLC.FACE-ENHANCER":
|
||||||
|
if modules.globals.fp_ui["face_enhancer"]:
|
||||||
|
temp_frame = frame_processor.process_frame(None, temp_frame)
|
||||||
|
else:
|
||||||
|
if current_source_image:
|
||||||
|
temp_frame = frame_processor.process_frame(current_source_image, temp_frame)
|
||||||
|
else:
|
||||||
|
modules.globals.target_path = None
|
||||||
|
for frame_processor in frame_processors:
|
||||||
|
if frame_processor.NAME == "DLC.FACE-ENHANCER":
|
||||||
|
if modules.globals.fp_ui["face_enhancer"]:
|
||||||
temp_frame = frame_processor.process_frame_v2(temp_frame)
|
temp_frame = frame_processor.process_frame_v2(temp_frame)
|
||||||
|
else:
|
||||||
|
temp_frame = frame_processor.process_frame_v2(temp_frame)
|
||||||
|
|
||||||
# Calculate and display FPS
|
current_time = time.time()
|
||||||
current_time = time.time()
|
fps_data["frame_count"] += 1
|
||||||
frame_count += 1
|
time_diff = current_time - fps_data["prev_time"]
|
||||||
if current_time - prev_time >= fps_update_interval:
|
|
||||||
fps = frame_count / (current_time - prev_time)
|
|
||||||
frame_count = 0
|
|
||||||
prev_time = current_time
|
|
||||||
|
|
||||||
if modules.globals.show_fps:
|
if time_diff >= fps_data.get("fps_update_interval", 0.5):
|
||||||
cv2.putText(
|
fps_data["fps"] = fps_data["frame_count"] / time_diff
|
||||||
temp_frame,
|
fps_data["frame_count"] = 0
|
||||||
f"FPS: {fps:.1f}",
|
fps_data["prev_time"] = current_time
|
||||||
(10, 30),
|
|
||||||
cv2.FONT_HERSHEY_SIMPLEX,
|
|
||||||
1,
|
|
||||||
(0, 255, 0),
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
image = cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB)
|
if modules.globals.show_fps:
|
||||||
image = Image.fromarray(image)
|
cv2.putText(
|
||||||
image = ImageOps.contain(
|
temp_frame,
|
||||||
image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS
|
f"FPS: {fps_data['fps']:.1f}",
|
||||||
|
(10, 30),
|
||||||
|
cv2.FONT_HERSHEY_SIMPLEX,
|
||||||
|
1,
|
||||||
|
(0, 255, 0),
|
||||||
|
2,
|
||||||
)
|
)
|
||||||
image = ctk.CTkImage(image, size=image.size)
|
|
||||||
preview_label.configure(image=image)
|
|
||||||
ROOT.update()
|
|
||||||
|
|
||||||
if PREVIEW.state() == "withdrawn":
|
if temp_frame is not None and temp_frame.size > 0:
|
||||||
break
|
image = cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB)
|
||||||
|
pil_image = Image.fromarray(image)
|
||||||
|
|
||||||
cap.release()
|
contained_image = ImageOps.contain(
|
||||||
PREVIEW.withdraw()
|
pil_image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS
|
||||||
|
)
|
||||||
|
ctk_image = ctk.CTkImage(contained_image, size=contained_image.size)
|
||||||
|
preview_label.configure(image=ctk_image)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
ROOT.after(delay_ms, lambda: update_webcam_frame_after(cap, frame_processors, source_image, fps_data, delay_ms))
|
||||||
|
|
||||||
|
|
||||||
def create_source_target_popup_for_webcam(
|
def create_source_target_popup_for_webcam(
|
||||||
|
|
Loading…
Reference in New Issue