1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
| import cv2 import numpy as np import time from typing import Tuple, List, Optional
class FaceDetector: """ 人脸检测模块 - 针对边缘硬件优化 方案1: YOLOv8n (精度优先, 需Edge TPU) 方案2: Haar Cascade (速度优先, CPU-only) 方案3: MediaPipe (平衡方案) 论文策略: 根据硬件平台选择不同检测器 """ def __init__(self, platform: str = 'rpi5'): self.platform = platform if platform == 'coral': from pycoral.adapters import detect from pycoral.utils.edgetpu import make_interpreter self.interpreter = make_interpreter('yolov8n_face_edgetpu.tflite') self.input_size = (320, 320) elif platform == 'rpi5': self.detector = cv2.dnn.readNetFromCaffe( 'deploy.prototxt', 'mobilenet_ssd.caffemodel' ) self.input_size = (300, 300) else: import mediapipe as mp self.mp_detection = mp.solutions.face_detection.FaceDetection( model_selection=0, min_detection_confidence=0.5 ) def detect(self, frame: np.ndarray) -> Optional[Tuple[int, int, int, int]]: """ 检测人脸并返回最大人脸的边界框 Args: frame: BGR图像 (H, W, 3) Returns: bbox: (x1, y1, x2, y2) 或 None """ h, w = frame.shape[:2] if self.platform == 'coral': return self._detect_coral(frame, w, h) elif self.platform == 'rpi5': return self._detect_rpi5(frame, w, h) else: return self._detect_mediapipe(frame, w, h) def _detect_rpi5(self, frame: np.ndarray, w: int, h: int) -> Optional[Tuple[int, int, int, int]]: """树莓派5 CPU检测 - 使用OpenCV DNN""" blob = cv2.dnn.blobFromImage( frame, 0.007843, self.input_size, (127.5, 127.5, 127.5), swapRB=False, crop=False ) self.detector.setInput(blob) detections = self.detector.forward() best_face = None best_area = 0 for i in range(detections.shape[2]): confidence = detections[0, 0, i, 2] if confidence > 0.5: class_id = int(detections[0, 0, i, 1]) if class_id == 15: x1 = int(detections[0, 0, i, 3] * w) y1 = int(detections[0, 0, i, 4] * h) x2 = int(detections[0, 0, i, 5] * w) y2 = int(detections[0, 0, i, 6] * h) area = (x2 - x1) * (y2 - y1) if area > best_area: best_area = area best_face = (x1, y1, x2, y2) return best_face def _detect_coral(self, frame: np.ndarray, w: int, h: int) -> Optional[Tuple[int, int, int, int]]: """Google Coral Edge TPU检测""" input_data = cv2.resize(frame, self.input_size) input_data = cv2.cvtColor(input_data, cv2.COLOR_BGR2RGB) input_data = input_data.reshape(1, *self.input_size, 3) input_data = input_data.astype(np.uint8) from pycoral.adapters import common common.set_input(self.interpreter, input_data) self.interpreter.invoke() from pycoral.adapters import detect results = detect.get_objects( self.interpreter, score_threshold=0.5 ) best_face = None best_area = 0 for obj in results: bbox = obj.bbox x1 = int(bbox.xmin * w / self.input_size[0]) y1 = int(bbox.ymin * h / self.input_size[1]) x2 = int(bbox.xmax * w / self.input_size[0]) y2 = int(bbox.ymax * h / self.input_size[1]) area = (x2 - x1) * (y2 - y1) if area > best_area: best_area = area best_face = (x1, y1, x2, y2) return best_face def _detect_mediapipe(self, frame: np.ndarray, w: int, h: int) -> Optional[Tuple[int, int, int, int]]: """MediaPipe检测(跨平台后备)""" rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = self.mp_detection.process(rgb) if not results.detections: return None best_face = None best_area = 0 for detection in results.detections: bbox = detection.location_data.relative_bounding_box x1 = int(bbox.xmin * w) y1 = int(bbox.ymin * h) x2 = int((bbox.xmin + bbox.width) * w) y2 = int((bbox.ymin + bbox.height) * h) area = (x2 - x1) * (y2 - y1) if area > best_area: best_area = area best_face = (x1, y1, x2, y2) return best_face
|