diff --git a/modules/capturer.py b/modules/capturer.py index a87cf4c..1dee22d 100644 --- a/modules/capturer.py +++ b/modules/capturer.py @@ -1,6 +1,7 @@ from typing import Any import cv2 import modules.globals # Import the globals to check the color correction toggle +from modules.gpu_processing import gpu_cvt_color def get_video_frame(video_path: str, frame_number: int = 0) -> Any: @@ -19,7 +20,7 @@ def get_video_frame(video_path: str, frame_number: int = 0) -> Any: if has_frame and modules.globals.color_correction: # Convert the frame color if necessary - frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + frame = gpu_cvt_color(frame, cv2.COLOR_BGR2RGB) capture.release() return frame if has_frame else None diff --git a/modules/gpu_processing.py b/modules/gpu_processing.py new file mode 100644 index 0000000..8c3e166 --- /dev/null +++ b/modules/gpu_processing.py @@ -0,0 +1,286 @@ +# --- START OF FILE gpu_processing.py --- +""" +GPU-accelerated image processing using OpenCV CUDA (cv2.cuda.GpuMat). + +Provides drop-in replacements for common cv2 functions. When OpenCV is built +with CUDA support the functions transparently upload → process → download via +GpuMat; otherwise they fall back to the regular CPU path so the rest of the +codebase never has to care whether CUDA is available. + +Usage +----- + from modules.gpu_processing import ( + gpu_gaussian_blur, gpu_sharpen, gpu_add_weighted, + gpu_resize, gpu_cvt_color, gpu_flip, + is_gpu_accelerated, + ) +""" + +from __future__ import annotations + +import cv2 +import numpy as np +from typing import Tuple, Optional + +# --------------------------------------------------------------------------- +# CUDA availability detection (evaluated once at import time) +# --------------------------------------------------------------------------- +CUDA_AVAILABLE: bool = False + +try: + # cv2.cuda.GpuMat is only present when OpenCV is compiled with CUDA + _test_mat = cv2.cuda.GpuMat() + # Verify we have the required filter / image-processing functions + _has_gauss = hasattr(cv2.cuda, "createGaussianFilter") + _has_resize = hasattr(cv2.cuda, "resize") + _has_cvt = hasattr(cv2.cuda, "cvtColor") + if _has_gauss and _has_resize and _has_cvt: + CUDA_AVAILABLE = True + print("[gpu_processing] OpenCV CUDA support detected – GPU-accelerated processing enabled.") + else: + missing = [] + if not _has_gauss: + missing.append("createGaussianFilter") + if not _has_resize: + missing.append("resize") + if not _has_cvt: + missing.append("cvtColor") + print(f"[gpu_processing] cv2.cuda.GpuMat exists but missing: {', '.join(missing)} – falling back to CPU.") +except Exception: + print("[gpu_processing] OpenCV CUDA not available – using CPU fallback for all operations.") + + +# --------------------------------------------------------------------------- +# Internal helpers +# --------------------------------------------------------------------------- + +def _ensure_uint8(img: np.ndarray) -> np.ndarray: + """Clip and convert to uint8 if necessary.""" + if img.dtype != np.uint8: + return np.clip(img, 0, 255).astype(np.uint8) + return img + + +def _ksize_odd(ksize: Tuple[int, int]) -> Tuple[int, int]: + """Ensure kernel dimensions are positive and odd (required by GaussianBlur).""" + kw = max(1, ksize[0] // 2 * 2 + 1) if ksize[0] > 0 else 0 + kh = max(1, ksize[1] // 2 * 2 + 1) if ksize[1] > 0 else 0 + return (kw, kh) + + +def _cv_type_for(img: np.ndarray) -> int: + """Return the OpenCV type constant matching *img* (uint8 only).""" + channels = 1 if img.ndim == 2 else img.shape[2] + if channels == 1: + return cv2.CV_8UC1 + elif channels == 3: + return cv2.CV_8UC3 + elif channels == 4: + return cv2.CV_8UC4 + return cv2.CV_8UC3 # fallback + + +# --------------------------------------------------------------------------- +# Public API – Gaussian Blur +# --------------------------------------------------------------------------- + +def gpu_gaussian_blur( + src: np.ndarray, + ksize: Tuple[int, int], + sigma_x: float, + sigma_y: float = 0, +) -> np.ndarray: + """Drop-in replacement for ``cv2.GaussianBlur`` with CUDA acceleration. + + Parameters match ``cv2.GaussianBlur(src, ksize, sigmaX, sigmaY)``. + When *ksize* is ``(0, 0)`` OpenCV computes the kernel size from *sigma_x*. + """ + if CUDA_AVAILABLE: + try: + src_u8 = _ensure_uint8(src) + cv_type = _cv_type_for(src_u8) + ks = _ksize_odd(ksize) if ksize != (0, 0) else ksize + + gauss = cv2.cuda.createGaussianFilter(cv_type, cv_type, ks, sigma_x, sigma_y) + gpu_src = cv2.cuda.GpuMat() + gpu_src.upload(src_u8) + gpu_dst = gauss.apply(gpu_src) + return gpu_dst.download() + except cv2.error: + pass + + return cv2.GaussianBlur(src, ksize, sigma_x, sigmaY=sigma_y) + + +# --------------------------------------------------------------------------- +# Public API – addWeighted +# --------------------------------------------------------------------------- + +def gpu_add_weighted( + src1: np.ndarray, + alpha: float, + src2: np.ndarray, + beta: float, + gamma: float, +) -> np.ndarray: + """Drop-in replacement for ``cv2.addWeighted`` with CUDA acceleration.""" + if CUDA_AVAILABLE: + try: + s1 = _ensure_uint8(src1) + s2 = _ensure_uint8(src2) + g1 = cv2.cuda.GpuMat() + g2 = cv2.cuda.GpuMat() + g1.upload(s1) + g2.upload(s2) + gpu_dst = cv2.cuda.addWeighted(g1, alpha, g2, beta, gamma) + return gpu_dst.download() + except cv2.error: + pass + + return cv2.addWeighted(src1, alpha, src2, beta, gamma) + + +# --------------------------------------------------------------------------- +# Public API – Unsharp-mask sharpening +# --------------------------------------------------------------------------- + +def gpu_sharpen( + src: np.ndarray, + strength: float, + sigma: float = 3, +) -> np.ndarray: + """Unsharp-mask sharpening, optionally GPU-accelerated. + + Equivalent to:: + + blurred = GaussianBlur(src, (0,0), sigma) + result = addWeighted(src, 1+strength, blurred, -strength, 0) + """ + if strength <= 0: + return src + + if CUDA_AVAILABLE: + try: + src_u8 = _ensure_uint8(src) + cv_type = _cv_type_for(src_u8) + + gauss = cv2.cuda.createGaussianFilter(cv_type, cv_type, (0, 0), sigma) + gpu_src = cv2.cuda.GpuMat() + gpu_src.upload(src_u8) + gpu_blurred = gauss.apply(gpu_src) + gpu_sharp = cv2.cuda.addWeighted(gpu_src, 1.0 + strength, gpu_blurred, -strength, 0) + result = gpu_sharp.download() + return np.clip(result, 0, 255).astype(np.uint8) + except cv2.error: + pass + + blurred = cv2.GaussianBlur(src, (0, 0), sigma) + sharpened = cv2.addWeighted(src, 1.0 + strength, blurred, -strength, 0) + return np.clip(sharpened, 0, 255).astype(np.uint8) + + +# --------------------------------------------------------------------------- +# Public API – Resize +# --------------------------------------------------------------------------- + +# Map common cv2 interpolation flags to their CUDA equivalents +_INTERP_MAP = { + cv2.INTER_NEAREST: cv2.INTER_NEAREST, + cv2.INTER_LINEAR: cv2.INTER_LINEAR, + cv2.INTER_CUBIC: cv2.INTER_CUBIC, + cv2.INTER_AREA: cv2.INTER_AREA, + cv2.INTER_LANCZOS4: cv2.INTER_LANCZOS4, +} + + +def gpu_resize( + src: np.ndarray, + dsize: Tuple[int, int], + fx: float = 0, + fy: float = 0, + interpolation: int = cv2.INTER_LINEAR, +) -> np.ndarray: + """Drop-in replacement for ``cv2.resize`` with CUDA acceleration. + + Parameters match ``cv2.resize(src, dsize, fx=fx, fy=fy, interpolation=...)``. + """ + if CUDA_AVAILABLE: + try: + src_u8 = _ensure_uint8(src) + gpu_src = cv2.cuda.GpuMat() + gpu_src.upload(src_u8) + + interp = _INTERP_MAP.get(interpolation, cv2.INTER_LINEAR) + + if dsize and dsize[0] > 0 and dsize[1] > 0: + gpu_dst = cv2.cuda.resize(gpu_src, dsize, interpolation=interp) + else: + gpu_dst = cv2.cuda.resize(gpu_src, (0, 0), fx=fx, fy=fy, interpolation=interp) + + return gpu_dst.download() + except cv2.error: + pass + + return cv2.resize(src, dsize, fx=fx, fy=fy, interpolation=interpolation) + + +# --------------------------------------------------------------------------- +# Public API – Color conversion +# --------------------------------------------------------------------------- + +def gpu_cvt_color( + src: np.ndarray, + code: int, +) -> np.ndarray: + """Drop-in replacement for ``cv2.cvtColor`` with CUDA acceleration. + + Parameters match ``cv2.cvtColor(src, code)``. + """ + if CUDA_AVAILABLE: + try: + src_u8 = _ensure_uint8(src) + gpu_src = cv2.cuda.GpuMat() + gpu_src.upload(src_u8) + gpu_dst = cv2.cuda.cvtColor(gpu_src, code) + return gpu_dst.download() + except cv2.error: + pass + + return cv2.cvtColor(src, code) + + +# --------------------------------------------------------------------------- +# Public API – Flip +# --------------------------------------------------------------------------- + +def gpu_flip( + src: np.ndarray, + flip_code: int, +) -> np.ndarray: + """Drop-in replacement for ``cv2.flip`` with CUDA acceleration. + + Parameters match ``cv2.flip(src, flipCode)``. + *flip_code*: 0 = vertical, 1 = horizontal, -1 = both. + """ + if CUDA_AVAILABLE: + try: + src_u8 = _ensure_uint8(src) + gpu_src = cv2.cuda.GpuMat() + gpu_src.upload(src_u8) + gpu_dst = cv2.cuda.flip(gpu_src, flip_code) + return gpu_dst.download() + except cv2.error: + pass + + return cv2.flip(src, flip_code) + + +# --------------------------------------------------------------------------- +# Convenience: check at runtime whether GPU path is active +# --------------------------------------------------------------------------- + +def is_gpu_accelerated() -> bool: + """Return ``True`` when the CUDA path will be used.""" + return CUDA_AVAILABLE + +# --- END OF FILE gpu_processing.py --- diff --git a/modules/predicter.py b/modules/predicter.py index 23a2564..2ba7d3b 100644 --- a/modules/predicter.py +++ b/modules/predicter.py @@ -3,6 +3,7 @@ import opennsfw2 from PIL import Image import cv2 # Add OpenCV import import modules.globals # Import globals to access the color correction toggle +from modules.gpu_processing import gpu_cvt_color from modules.typing import Frame @@ -14,7 +15,7 @@ model = None def predict_frame(target_frame: Frame) -> bool: # Convert the frame to RGB before processing if color correction is enabled if modules.globals.color_correction: - target_frame = cv2.cvtColor(target_frame, cv2.COLOR_BGR2RGB) + target_frame = gpu_cvt_color(target_frame, cv2.COLOR_BGR2RGB) image = Image.fromarray(target_frame) image = opennsfw2.preprocess_image(image, opennsfw2.Preprocessing.YAHOO) diff --git a/modules/processors/frame/face_masking.py b/modules/processors/frame/face_masking.py index 2566116..2329818 100644 --- a/modules/processors/frame/face_masking.py +++ b/modules/processors/frame/face_masking.py @@ -2,6 +2,7 @@ import cv2 import numpy as np from modules.typing import Face, Frame import modules.globals +from modules.gpu_processing import gpu_gaussian_blur, gpu_resize, gpu_cvt_color def apply_color_transfer(source, target): """ @@ -61,8 +62,8 @@ def create_face_mask(face: Face, frame: Frame) -> np.ndarray: # Fill the padded convex hull cv2.fillConvexPoly(mask, hull_padded, 255) - # Smooth the mask edges - mask = cv2.GaussianBlur(mask, (5, 5), 3) + # Smooth the mask edges (GPU-accelerated when available) + mask = gpu_gaussian_blur(mask, (5, 5), 3) return mask @@ -123,8 +124,8 @@ def create_lower_mouth_mask( polygon_relative_to_roi = expanded_landmarks - [min_x, min_y] cv2.fillPoly(mask_roi, [polygon_relative_to_roi], 255) - # Apply Gaussian blur to soften the mask edges - mask_roi = cv2.GaussianBlur(mask_roi, (15, 15), 5) + # Apply Gaussian blur to soften the mask edges (GPU-accelerated when available) + mask_roi = gpu_gaussian_blur(mask_roi, (15, 15), 5) # Place the mask ROI in the full-sized mask mask[min_y:max_y, min_x:max_x] = mask_roi @@ -192,8 +193,8 @@ def create_eyes_mask(face: Face, frame: Frame) -> (np.ndarray, np.ndarray, tuple cv2.ellipse(mask_roi, left_center, left_axes, 0, 0, 360, 255, -1) cv2.ellipse(mask_roi, right_center, right_axes, 0, 0, 360, 255, -1) - # Apply Gaussian blur to soften mask edges - mask_roi = cv2.GaussianBlur(mask_roi, (15, 15), 5) + # Apply Gaussian blur to soften mask edges (GPU-accelerated when available) + mask_roi = gpu_gaussian_blur(mask_roi, (15, 15), 5) # Place the mask ROI in the full-sized mask mask[min_y:max_y, min_x:max_x] = mask_roi @@ -374,15 +375,15 @@ def create_eyebrows_mask(face: Face, frame: Frame) -> (np.ndarray, np.ndarray, t left_shape = create_curved_eyebrow(left_local) right_shape = create_curved_eyebrow(right_local) - # Apply multi-stage blurring for natural feathering + # Apply multi-stage blurring for natural feathering (GPU-accelerated when available) # First, strong Gaussian blur for initial softening - mask_roi = cv2.GaussianBlur(mask_roi, (21, 21), 7) + mask_roi = gpu_gaussian_blur(mask_roi, (21, 21), 7) # Second, medium blur for transition areas - mask_roi = cv2.GaussianBlur(mask_roi, (11, 11), 3) + mask_roi = gpu_gaussian_blur(mask_roi, (11, 11), 3) # Finally, light blur for fine details - mask_roi = cv2.GaussianBlur(mask_roi, (5, 5), 1) + mask_roi = gpu_gaussian_blur(mask_roi, (5, 5), 1) # Normalize mask values mask_roi = cv2.normalize(mask_roi, None, 0, 255, cv2.NORM_MINMAX) @@ -405,7 +406,7 @@ def create_eyebrows_mask(face: Face, frame: Frame) -> (np.ndarray, np.ndarray, t 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_roi = gpu_gaussian_blur(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) @@ -433,11 +434,11 @@ def apply_mask_area( return frame try: - resized_cutout = cv2.resize(cutout, (box_width, box_height)) + resized_cutout = gpu_resize(cutout, (box_width, box_height)) roi = frame[min_y:max_y, min_x:max_x] if roi.shape != resized_cutout.shape: - resized_cutout = cv2.resize( + resized_cutout = gpu_resize( resized_cutout, (roi.shape[1], roi.shape[0]) ) @@ -457,8 +458,8 @@ def apply_mask_area( adjusted_polygon = polygon - [min_x, min_y] cv2.fillPoly(polygon_mask, [adjusted_polygon], 255) - # Apply strong initial feathering - polygon_mask = cv2.GaussianBlur(polygon_mask, (21, 21), 7) + # Apply strong initial feathering (GPU-accelerated when available) + polygon_mask = gpu_gaussian_blur(polygon_mask, (21, 21), 7) # Apply additional feathering feather_amount = min( diff --git a/modules/processors/frame/face_swapper.py b/modules/processors/frame/face_swapper.py index 40b3401..c2afcf1 100644 --- a/modules/processors/frame/face_swapper.py +++ b/modules/processors/frame/face_swapper.py @@ -15,6 +15,7 @@ from modules.utilities import ( is_video, ) from modules.cluster_analysis import find_closest_centroid +from modules.gpu_processing import gpu_gaussian_blur, gpu_sharpen, gpu_add_weighted, gpu_resize, gpu_cvt_color import os from collections import deque import time @@ -158,7 +159,7 @@ def swap_face(source_face: Face, target_face: Face, temp_frame: Frame) -> Frame: # print(f"Warning: Swapped frame shape {swapped_frame_raw.shape} differs from input {temp_frame.shape}.") # Debug # Attempt resize (might distort if aspect ratio changed, but better than crashing) try: - swapped_frame_raw = cv2.resize(swapped_frame_raw, (temp_frame.shape[1], temp_frame.shape[0])) + swapped_frame_raw = gpu_resize(swapped_frame_raw, (temp_frame.shape[1], temp_frame.shape[0])) except Exception as resize_e: # print(f"Error resizing swapped frame: {resize_e}") # Debug return original_frame @@ -236,7 +237,7 @@ def swap_face(source_face: Face, target_face: Face, temp_frame: Frame) -> Frame: # Blend the original_frame with the (potentially mouth-masked) swapped_frame # Ensure both frames are uint8 before blending - final_swapped_frame = cv2.addWeighted(original_frame.astype(np.uint8), 1 - opacity, swapped_frame.astype(np.uint8), opacity, 0) + final_swapped_frame = gpu_add_weighted(original_frame.astype(np.uint8), 1 - opacity, swapped_frame.astype(np.uint8), opacity, 0) # Ensure final frame is uint8 after blending (addWeighted should preserve it, but belt-and-suspenders) final_swapped_frame = final_swapped_frame.astype(np.uint8) @@ -312,17 +313,10 @@ def apply_post_processing(current_frame: Frame, swapped_face_bboxes: List[np.nda face_region = processed_frame[y1:y2, x1:x2] if face_region.size == 0: continue - # Apply sharpening with optimized parameters for Apple Silicon + # Apply sharpening (GPU-accelerated when CUDA OpenCV is available) try: - # Use smaller sigma for faster processing on Apple Silicon sigma = 2 if IS_APPLE_SILICON else 3 - blurred = cv2.GaussianBlur(face_region, (0, 0), sigma) - sharpened_region = cv2.addWeighted( - face_region, 1.0 + sharpness_value, - blurred, -sharpness_value, - 0 - ) - sharpened_region = np.clip(sharpened_region, 0, 255).astype(np.uint8) + sharpened_region = gpu_sharpen(face_region, strength=sharpness_value, sigma=sigma) processed_frame[y1:y2, x1:x2] = sharpened_region except cv2.error: pass @@ -338,7 +332,7 @@ def apply_post_processing(current_frame: Frame, swapped_face_bboxes: List[np.nda if PREVIOUS_FRAME_RESULT is not None and PREVIOUS_FRAME_RESULT.shape == processed_frame.shape and PREVIOUS_FRAME_RESULT.dtype == processed_frame.dtype: # Perform interpolation try: - final_frame = cv2.addWeighted( + final_frame = gpu_add_weighted( PREVIOUS_FRAME_RESULT, 1.0 - interpolation_weight, processed_frame, interpolation_weight, 0 @@ -813,10 +807,10 @@ def create_lower_mouth_mask( # Draw polygon on the ROI mask cv2.fillPoly(mask_roi, [polygon_relative_to_roi], 255) - # Apply Gaussian blur (ensure kernel size is odd and positive) + # Apply Gaussian blur (GPU-accelerated when available) blur_k_size = getattr(modules.globals, "mask_blur_kernel", 15) # Default 15 blur_k_size = max(1, blur_k_size // 2 * 2 + 1) # Ensure odd - mask_roi = cv2.GaussianBlur(mask_roi, (blur_k_size, blur_k_size), 0) # Sigma=0 calculates from kernel + mask_roi = gpu_gaussian_blur(mask_roi, (blur_k_size, blur_k_size), 0) # Place the mask ROI in the full-sized mask mask[min_y:max_y, min_x:max_x] = mask_roi @@ -952,7 +946,7 @@ def apply_mouth_area( if roi.shape[:2] != mouth_cutout.shape[:2]: # Check if mouth_cutout has valid dimensions before resizing if mouth_cutout.shape[0] > 0 and mouth_cutout.shape[1] > 0: - resized_mouth_cutout = cv2.resize(mouth_cutout, (box_width, box_height), interpolation=cv2.INTER_LINEAR) + resized_mouth_cutout = gpu_resize(mouth_cutout, (box_width, box_height), interpolation=cv2.INTER_LINEAR) else: # print("Warning: mouth_cutout has invalid dimensions, cannot resize.") return frame # Cannot proceed without valid cutout @@ -1125,14 +1119,10 @@ def create_face_mask(face: Face, frame: Frame) -> np.ndarray: return mask # Return empty mask on error - # Apply Gaussian blur to feather the mask edges - # Kernel size should be reasonably large, odd, and positive + # Apply Gaussian blur to feather the mask edges (GPU-accelerated when available) blur_k_size = getattr(modules.globals, "face_mask_blur", 31) # Default 31 blur_k_size = max(1, blur_k_size // 2 * 2 + 1) # Ensure odd and positive - - # Use sigma=0 to let OpenCV calculate from kernel size - # Apply blur to the uint8 mask directly - mask = cv2.GaussianBlur(mask, (blur_k_size, blur_k_size), 0) + mask = gpu_gaussian_blur(mask, (blur_k_size, blur_k_size), 0) # --- Optional: Return float mask for apply_mouth_area --- # mask = mask.astype(float) / 255.0 diff --git a/modules/ui.py b/modules/ui.py index b2a4d99..8cd0fe7 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -4,6 +4,7 @@ import customtkinter as ctk from typing import Callable, Tuple import cv2 from cv2_enumerate_cameras import enumerate_cameras # Add this import +from modules.gpu_processing import gpu_cvt_color, gpu_resize, gpu_flip from PIL import Image, ImageOps import time import json @@ -546,7 +547,7 @@ def create_source_target_popup( ) x_label.grid(row=id, column=2, padx=10, pady=10) - image = Image.fromarray(cv2.cvtColor(item["target"]["cv2"], cv2.COLOR_BGR2RGB)) + image = Image.fromarray(gpu_cvt_color(item["target"]["cv2"], cv2.COLOR_BGR2RGB)) image = image.resize( (MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS ) @@ -601,7 +602,7 @@ def update_popup_source( } image = Image.fromarray( - cv2.cvtColor(map[button_num]["source"]["cv2"], cv2.COLOR_BGR2RGB) + gpu_cvt_color(map[button_num]["source"]["cv2"], cv2.COLOR_BGR2RGB) ) image = image.resize( (MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS @@ -794,7 +795,7 @@ def fit_image_to_size(image, width: int, height: int): ratio_w = width / w ratio = max(ratio_w, ratio_h) new_size = (int(ratio * w), int(ratio * h)) - return cv2.resize(image, dsize=new_size) + return gpu_resize(image, dsize=new_size) def render_image_preview(image_path: str, size: Tuple[int, int]) -> ctk.CTkImage: @@ -812,7 +813,7 @@ def render_video_preview( capture.set(cv2.CAP_PROP_POS_FRAMES, frame_number) has_frame, frame = capture.read() if has_frame: - image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) + image = Image.fromarray(gpu_cvt_color(frame, cv2.COLOR_BGR2RGB)) if size: image = ImageOps.fit(image, size, Image.LANCZOS) return ctk.CTkImage(image, size=image.size) @@ -850,7 +851,7 @@ def update_preview(frame_number: int = 0) -> None: temp_frame = frame_processor.process_frame( get_one_face(cv2.imread(modules.globals.source_path)), temp_frame ) - image = Image.fromarray(cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB)) + image = Image.fromarray(gpu_cvt_color(temp_frame, cv2.COLOR_BGR2RGB)) image = ImageOps.contain( image, (PREVIEW_MAX_WIDTH, PREVIEW_MAX_HEIGHT), Image.LANCZOS ) @@ -1007,7 +1008,7 @@ def _processing_thread_func(capture_queue, processed_queue, stop_event): proc_frame_index += 1 if modules.globals.live_mirror: - temp_frame = cv2.flip(temp_frame, 1) + temp_frame = gpu_flip(temp_frame, 1) if not modules.globals.map_faces: if source_image is None and modules.globals.source_path: @@ -1136,7 +1137,7 @@ def create_webcam_preview(camera_index: int): temp_frame, PREVIEW.winfo_width(), PREVIEW.winfo_height() ) - image = cv2.cvtColor(temp_frame, cv2.COLOR_BGR2RGB) + image = gpu_cvt_color(temp_frame, cv2.COLOR_BGR2RGB) image = Image.fromarray(image) image = ImageOps.contain( image, (temp_frame.shape[1], temp_frame.shape[0]), Image.LANCZOS @@ -1263,7 +1264,7 @@ def refresh_data(map: list): if "source" in item: image = Image.fromarray( - cv2.cvtColor(item["source"]["cv2"], cv2.COLOR_BGR2RGB) + gpu_cvt_color(item["source"]["cv2"], cv2.COLOR_BGR2RGB) ) image = image.resize( (MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS @@ -1281,7 +1282,7 @@ def refresh_data(map: list): if "target" in item: image = Image.fromarray( - cv2.cvtColor(item["target"]["cv2"], cv2.COLOR_BGR2RGB) + gpu_cvt_color(item["target"]["cv2"], cv2.COLOR_BGR2RGB) ) image = image.resize( (MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS @@ -1329,7 +1330,7 @@ def update_webcam_source( } image = Image.fromarray( - cv2.cvtColor(map[button_num]["source"]["cv2"], cv2.COLOR_BGR2RGB) + gpu_cvt_color(map[button_num]["source"]["cv2"], cv2.COLOR_BGR2RGB) ) image = image.resize( (MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS @@ -1381,7 +1382,7 @@ def update_webcam_target( } image = Image.fromarray( - cv2.cvtColor(map[button_num]["target"]["cv2"], cv2.COLOR_BGR2RGB) + gpu_cvt_color(map[button_num]["target"]["cv2"], cv2.COLOR_BGR2RGB) ) image = image.resize( (MAPPER_PREVIEW_MAX_WIDTH, MAPPER_PREVIEW_MAX_HEIGHT), Image.LANCZOS