Opencv2+DroidCam实现Python访问手机摄像头手机当监控终端

前提

电脑手机在同一wifi下,Python,Opencv库

app准备:DroidCam https://www.dev47apps.com/

下载之后打开app简要看一下说明,然后进入到app主界面,可以看到如下场景

图片[1]-Opencv2+DroidCam实现Python访问手机摄像头手机当监控终端

在浏览器中输入这两个网址之一便可以访问到手机摄像头的录像画面了

图片[2]-Opencv2+DroidCam实现Python访问手机摄像头手机当监控终端

底下那个多一个video后缀的网址打开来之后只有一个视频界面

python调用代码

# -*- coding: utf-8 -*-
import cv2
import numpy as np
import time
import os
import subprocess
import sys
import math
from collections import deque

class FallDetectionSystem:
    def __init__(self):
        # 检查并安装依赖
        self.install_dependencies()
        
        # 初始化 DroidCam 视频流
        self.droidcam_url = 'http://192.168.137.175:4747/video'
        self.cap = cv2.VideoCapture(self.droidcam_url)
        
        # 检查视频流是否成功打开
        if not self.cap.isOpened():
            print(f"错误:无法连接到 DroidCam 视频流 ({self.droidcam_url})")
            print("请检查:1. DroidCam 是否运行 2. IP/端口是否正确 3. 网络连接")
            return
        
        # 设置视频分辨率
        self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
        
        # 获取实际分辨率
        width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        print(f"视频流分辨率: {width}x{height}")
        
        # 加载检测器
        self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
        self.smile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_smile.xml')
        
        # 情绪颜色映射
        self.emotion_colors = {
            'Happy': (0, 255, 0),
            'Neutral': (128, 128, 128),
            'Unknown': (255, 255, 255)
        }
        
        # 性能统计
        self.fps_counter = 0
        self.start_time = time.time()
        
        # 统计数据
        self.total_faces_detected = 0
        self.emotion_stats = {'Happy': 0, 'Neutral': 0, 'Unknown': 0}
        
        # 运动检测
        self.background_subtractor = cv2.createBackgroundSubtractorMOG2(detectShadows=True)
        self.motion_threshold = 1000
        
        # 摔倒检测相关
        self.fall_detected = False
        self.fall_alert_time = 0
        self.fall_alert_duration = 3
        self.fall_count = 0
        self.last_fall_time = 0
        self.fall_cooldown = 10
        
        # 人体姿态分析
        self.person_positions = []
        self.position_history_size = 10
        
        # 检测模式
        self.detection_modes = ['All Detection', 'Face Only', 'Fall Detection Only']
        self.current_mode = 0
        
        # 左右晃手检测
        self.hand_positions = deque(maxlen=20)  # 存储最近20帧的手部中心点
        self.wave_threshold = 50  # 水平移动阈值(像素)
        self.wave_min_changes = 2  # 至少2次方向变化
        self.wave_time_window = 1.0  # 时间窗口(秒)

    def install_dependencies(self):
        """检查并安装必要的依赖包"""
        required_packages = ['opencv-python', 'numpy']
        for package in required_packages:
            try:
                __import__(package.replace('-', '_'))
            except ImportError:
                print(f"正在安装 {package}...")
                try:
                    subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
                    print(f"✓ {package} 安装成功")
                except subprocess.CalledProcessError:
                    print(f"✗ {package} 安装失败")
    
    def detect_faces(self, frame):
        """检测人脸"""
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        faces = self.face_cascade.detectMultiScale(
            gray,
            scaleFactor=1.1,
            minNeighbors=5,
            minSize=(30, 30)
        )
        return faces
    
    def detect_emotion(self, frame, face_rect):
        """简单的情绪检测"""
        x, y, w, h = face_rect
        roi_gray = cv2.cvtColor(frame[y:y+h, x:x+w], cv2.COLOR_BGR2GRAY)
        smiles = self.smile_cascade.detectMultiScale(roi_gray, 1.8, 20)
        if len(smiles) > 0:
            return 'Happy', 0.8
        else:
            return 'Neutral', 0.6
    
    def detect_skin_color(self, frame):
        """检测肤色区域"""
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        lower_skin = np.array([0, 20, 70], dtype=np.uint8)
        upper_skin = np.array([20, 255, 255], dtype=np.uint8)
        mask = cv2.inRange(hsv, lower_skin, upper_skin)
        kernel = np.ones((3, 3), np.uint8)
        mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
        mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
        return mask
    
    def detect_thumbs_up(self, frame):
        """检测大拇指手势"""
        skin_mask = self.detect_skin_color(frame)
        contours, _ = cv2.findContours(skin_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        thumbs_up_detected = False
        thumbs_positions = []
        for contour in contours:
            if cv2.contourArea(contour) < 5000:
                continue
            x, y, w, h = cv2.boundingRect(contour)
            if w < 30 or h < 60:
                continue
            aspect_ratio = float(h) / w if w > 0 else 0
            if aspect_ratio < 1.8 or aspect_ratio > 4.0:
                continue
            if y > frame.shape[0] * 0.7:
                continue
            hull = cv2.convexHull(contour, returnPoints=False)
            if len(hull) > 3:
                defects = cv2.convexityDefects(contour, hull)
                if defects is not None:
                    significant_defects = 0
                    for i in range(defects.shape[0]):
                        s, e, f, d = defects[i, 0]
                        if d > 12000:
                            significant_defects += 1
                    if significant_defects == 1:
                        area = cv2.contourArea(contour)
                        perimeter = cv2.arcLength(contour, True)
                        if perimeter > 0:
                            compactness = (perimeter * perimeter) / area
                            if 15 < compactness < 50:
                                thumbs_up_detected = True
                                thumbs_positions.append((x, y, w, h))
                                break
        return thumbs_up_detected, thumbs_positions
    
    def detect_hand_wave(self, frame):
        """检测左右晃手"""
        skin_mask = self.detect_skin_color(frame)
        contours, _ = cv2.findContours(skin_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        wave_detected = False
        wave_positions = []
        
        for contour in contours:
            area = cv2.contourArea(contour)
            if area < 5000 or area > 50000:  # 限制手部区域大小
                continue
            x, y, w, h = cv2.boundingRect(contour)
            if w < 30 or h < 30 or w > h * 2:  # 手部形状限制
                continue
            if y > frame.shape[0] * 0.7:  # 不在画面底部
                continue
            
            # 计算手部中心点
            center_x = x + w // 2
            center_y = y + h // 2
            current_time = time.time()
            
            # 记录手部位置和时间
            self.hand_positions.append((center_x, current_time))
            
            # 分析最近的手部位置
            if len(self.hand_positions) >= 5:  # 至少需要5帧来判断晃动
                # 筛选时间窗口内的位置
                valid_positions = [(x, t) for x, t in self.hand_positions if current_time - t <= self.wave_time_window]
                
                if len(valid_positions) >= 5:
                    x_coords = [x for x, _ in valid_positions]
                    # 计算水平移动范围
                    x_range = max(x_coords) - min(x_coords)
                    
                    # 检测方向变化
                    direction_changes = 0
                    for i in range(1, len(x_coords) - 1):
                        if (x_coords[i] > x_coords[i-1] and x_coords[i] > x_coords[i+1]) or \
                           (x_coords[i] < x_coords[i-1] and x_coords[i] < x_coords[i+1]):
                            direction_changes += 1
                    
                    if x_range > self.wave_threshold and direction_changes >= self.wave_min_changes:
                        wave_detected = True
                        wave_positions.append((x, y, w, h))
                        break
        
        return wave_detected, wave_positions
    
    def detect_motion_and_fall(self, frame):
        """检测运动和摔倒"""
        fg_mask = self.background_subtractor.apply(frame)
        kernel = np.ones((5, 5), np.uint8)
        fg_mask = cv2.morphologyEx(fg_mask, cv2.MORPH_CLOSE, kernel)
        fg_mask = cv2.morphologyEx(fg_mask, cv2.MORPH_OPEN, kernel)
        contours, _ = cv2.findContours(fg_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        motion_areas = []
        fall_risk = False
        
        for contour in contours:
            if cv2.contourArea(contour) > self.motion_threshold:
                x, y, w, h = cv2.boundingRect(contour)
                motion_areas.append((x, y, w, h))
                aspect_ratio = float(w) / h if h > 0 else 0
                if aspect_ratio > 2.0 and y > frame.shape[0] * 0.6:
                    fall_risk = True
        
        # 检测大拇指手势
        thumbs_up, thumbs_positions = self.detect_thumbs_up(frame)
        
        # 检测左右晃手
        wave_detected, wave_positions = self.detect_hand_wave(frame)
        
        current_time = time.time()
        fall_trigger_type = None
        
        # 处理大拇指手势触发
        if thumbs_up and len(thumbs_positions) > 0 and (current_time - self.last_fall_time) > self.fall_cooldown:
            valid_thumbs = False
            for (x, y, w, h) in thumbs_positions:
                if (w > 30 and h > 60 and y < frame.shape[0] * 0.7 and x > 50 and x < frame.shape[1] - 50):
                    valid_thumbs = True
                    break
            if valid_thumbs:
                fall_trigger_type = "Thumbs Up"
                self.trigger_fall_alert(frame, fall_trigger_type)
                self.last_fall_time = current_time
                fall_risk = True
        
        # 处理左右晃手触发
        if wave_detected and len(wave_positions) > 0 and (current_time - self.last_fall_time) > self.fall_cooldown:
            valid_wave = False
            for (x, y, w, h) in wave_positions:
                if (w > 30 and h > 30 and y < frame.shape[0] * 0.7 and x > 50 and x < frame.shape[1] - 50):
                    valid_wave = True
                    break
            if valid_wave:
                fall_trigger_type = "Hand Wave"
                self.trigger_fall_alert(frame, fall_trigger_type)
                self.last_fall_time = current_time
                fall_risk = True
        
        return motion_areas, fall_risk, thumbs_positions, fg_mask, wave_positions
    
    def trigger_fall_alert(self, frame, trigger_type):
        """触发摔倒警报"""
        self.fall_detected = True
        self.fall_alert_time = time.time()
        self.fall_count += 1
        self.save_emergency_screenshot(frame, trigger_type)
        print(f"🚨 摔倒警报!检测到紧急情况!(触发方式:{trigger_type})")
        print(f"摔倒次数: {self.fall_count}")
    
    def save_emergency_screenshot(self, frame, trigger_type):
        """保存紧急截图"""
        save_dir = "D:/demoimg/emergency"
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
        
        timestamp = time.strftime("%Y%m%d_%H%M%S")
        filename = os.path.join(save_dir, f"EMERGENCY_FALL_{timestamp}.jpg")
        emergency_frame = frame.copy()
        cv2.rectangle(emergency_frame, (0, 0), (frame.shape[1]-1, frame.shape[0]-1), (0, 0, 255), 5)
        cv2.putText(emergency_frame, "EMERGENCY - FALL DETECTED!", (50, 50), 
                   cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 3)
        cv2.putText(emergency_frame, f"Time: {time.strftime('%Y-%m-%d %H:%M:%S')}", (50, 100), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
        cv2.putText(emergency_frame, f"Fall Count: {self.fall_count}", (50, 150), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
        cv2.putText(emergency_frame, f"Trigger: {trigger_type}", (50, 200), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
        cv2.imwrite(filename, emergency_frame)
        
        info_filename = os.path.join(save_dir, f"EMERGENCY_INFO_{timestamp}.txt")
        with open(info_filename, 'w', encoding='utf-8') as f:
            f.write("🚨 紧急情况报告 🚨\n")
            f.write("=" * 30 + "\n")
            f.write(f"检测时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"事件类型: 摔倒检测\n")
            f.write(f"摔倒次数: {self.fall_count}\n")
            f.write(f"检测方式: {trigger_type}\n")
            f.write(f"图片文件: {filename}\n")
            f.write("\n注意:这是模拟摔倒检测,实际使用大拇指手势或左右晃手触发\n")
        
        print(f"🚨 紧急截图已保存: {filename}")
        print(f"📄 紧急信息已保存: {info_filename}")
    
    def draw_face_and_emotion(self, frame, faces):
        """绘制人脸边框和情绪"""
        face_emotions = []
        for i, (x, y, w, h) in enumerate(faces):
            emotion, confidence = self.detect_emotion(frame, (x, y, w, h))
            face_emotions.append(emotion)
            color = self.emotion_colors.get(emotion, (255, 255, 255))
            cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
            label = f'Face {i+1}: {emotion}'
            cv2.putText(frame, label, (x, y - 10), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
            center_x, center_y = x + w//2, y + h//2
            self.draw_emotion_icon(frame, center_x, center_y, emotion)
            self.emotion_stats[emotion] += 1
        return frame, face_emotions
    
    def draw_motion_and_fall(self, frame, motion_areas, fall_risk, thumbs_positions, wave_positions):
        """绘制运动检测和摔倒警报"""
        for (x, y, w, h) in motion_areas:
            color = (0, 0, 255) if fall_risk else (0, 255, 255)
            cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
            label = 'Fall Risk!' if fall_risk else 'Motion'
            cv2.putText(frame, label, (x, y - 10), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
        
        for (x, y, w, h) in thumbs_positions:
            cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 255), 2)
            cv2.putText(frame, 'Thumbs Up - Fall Signal!', (x, y - 10), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)
        
        for (x, y, w, h) in wave_positions:
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 255), 2)
            cv2.putText(frame, 'Wave Detected - Fall Signal!', (x, y - 10), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
        
        if self.fall_detected:
            current_time = time.time()
            if (current_time - self.fall_alert_time) < self.fall_alert_duration:
                if int((current_time - self.fall_alert_time) * 4) % 2 == 0:
                    overlay = frame.copy()
                    cv2.rectangle(overlay, (0, 0), (frame.shape[1], frame.shape[0]), (0, 0, 255), -1)
                    cv2.addWeighted(overlay, 0.3, frame, 0.7, 0, frame)
                cv2.putText(frame, "🚨 FALL DETECTED! 🚨", (frame.shape[1]//2 - 150, frame.shape[0]//2), 
                           cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 255), 3)
            else:
                self.fall_detected = False
        return frame
    
    def draw_emotion_icon(self, frame, center_x, center_y, emotion):
        """绘制情绪图标"""
        if emotion == 'Happy':
            cv2.circle(frame, (center_x, center_y), 15, (0, 255, 0), 2)
            cv2.circle(frame, (center_x-5, center_y-3), 2, (0, 255, 0), -1)
            cv2.circle(frame, (center_x+5, center_y-3), 2, (0, 255, 0), -1)
            cv2.ellipse(frame, (center_x, center_y+5), (8, 4), 0, 0, 180, (0, 255, 0), 2)
        elif emotion == 'Neutral':
            cv2.circle(frame, (center_x, center_y), 15, (128, 128, 128), 2)
            cv2.circle(frame, (center_x-5, center_y-3), 2, (128, 128, 128), -1)
            cv2.circle(frame, (center_x+5, center_y-3), 2, (128, 128, 128), -1)
            cv2.line(frame, (center_x-6, center_y+6), (center_x+6, center_y+6), (128, 128, 128), 2)
    
    def calculate_fps(self):
        """计算FPS"""
        self.fps_counter += 1
        elapsed_time = time.time() - self.start_time
        if elapsed_time >= 1.0:
            fps = self.fps_counter / elapsed_time
            self.fps_counter = 0
            self.start_time = time.time()
            return fps
        return None
    
    def add_info_display(self, frame, faces, face_emotions, motion_areas, fall_risk, fps=None):
        """添加信息显示"""
        overlay = frame.copy()
        cv2.rectangle(overlay, (10, 10), (400, 220), (0, 0, 0), -1)
        cv2.addWeighted(overlay, 0.7, frame, 0.3, 0, frame)
        
        mode_text = f"Mode: {self.detection_modes[self.current_mode]}"
        cv2.putText(frame, mode_text, (20, 30), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 0), 2)
        
        face_count = len(faces)
        motion_count = len(motion_areas)
        cv2.putText(frame, f"Faces: {face_count}", (20, 55), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
        cv2.putText(frame, f"Motion Areas: {motion_count}", (20, 75), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
        
        fall_status = "HIGH RISK!" if fall_risk else "Normal"
        fall_color = (0, 0, 255) if fall_risk else (0, 255, 0)
        cv2.putText(frame, f"Fall Status: {fall_status}", (20, 95), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, fall_color, 2)
        
        cv2.putText(frame, f"Fall Count: {self.fall_count}", (20, 115), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
        
        if fps is not None:
            cv2.putText(frame, f"FPS: {fps:.1f}", (20, 135), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
        
        if face_emotions:
            emotions_text = ", ".join(face_emotions)
            cv2.putText(frame, f"Emotions: {emotions_text}", (20, 155), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
        
        cv2.putText(frame, "SPACE: Switch Mode | q: Quit | s: Save", (20, 175), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
        cv2.putText(frame, "Thumbs Up or Hand Wave = Fall Signal", (20, 195), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 0, 255), 1)
        cv2.putText(frame, "Debug: Thumbs-up & wave detection enabled", (20, 215), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.3, (128, 128, 128), 1)
        
        self.add_statistics_display(frame)
        self.add_time_display(frame)
        return frame
    
    def add_statistics_display(self, frame):
        """添加统计显示"""
        overlay = frame.copy()
        cv2.rectangle(overlay, (frame.shape[1] - 220, 10), (frame.shape[1] - 10, 150), (0, 0, 0), -1)
        cv2.addWeighted(overlay, 0.7, frame, 0.3, 0, frame)
        
        cv2.putText(frame, "Fall Detection Stats:", (frame.shape[1] - 210, 30), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
        cv2.putText(frame, f"Total Falls: {self.fall_count}", (frame.shape[1] - 210, 55), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
        
        cv2.putText(frame, "Emotion Stats:", (frame.shape[1] - 210, 85), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
        
        y_offset = 105
        for emotion, count in self.emotion_stats.items():
            if count > 0:
                color = self.emotion_colors.get(emotion, (255, 255, 255))
                text = f"{emotion}: {count}"
                cv2.putText(frame, text, (frame.shape[1] - 210, y_offset), 
                           cv2.FONT_HERSHEY_SIMPLEX, 0.4, color, 1)
                y_offset += 20
    
    def add_time_display(self, frame):
        """添加时间显示到右下角"""
        current_time = time.strftime("%Y-%m-%d %H:%M:%S")
        (text_width, text_height), _ = cv2.getTextSize(current_time, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)
        x = frame.shape[1] - text_width - 20
        y = frame.shape[0] - 20
        overlay = frame.copy()
        cv2.rectangle(overlay, (x - 10, y - text_height - 10), (x + text_width + 10, y + 10), (0, 0, 0), -1)
        cv2.addWeighted(overlay, 0.7, frame, 0.3, 0, frame)
        cv2.putText(frame, current_time, (x, y), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
    
    def save_screenshot(self, frame, faces, face_emotions, motion_areas, fall_risk):
        """保存普通截图和检测信息"""
        save_dir = "D:/demoimg"
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
        
        timestamp = time.strftime("%Y%m%d_%H%M%S")
        filename = os.path.join(save_dir, f"fall_detection_{timestamp}.jpg")
        cv2.imwrite(filename, frame)
        
        info_filename = os.path.join(save_dir, f"detection_info_{timestamp}.txt")
        with open(info_filename, 'w', encoding='utf-8') as f:
            f.write(f"摔倒检测系统截图时间: {time.strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write(f"检测模式: {self.detection_modes[self.current_mode]}\n\n")
            f.write(f"检测到人脸数量: {len(faces)}\n")
            for i, ((x, y, w, h), emotion) in enumerate(zip(faces, face_emotions)):
                f.write(f"人脸 {i+1}: 位置({x}, {y}), 大小({w}x{h}), 情绪: {emotion}\n")
            f.write(f"\n检测到运动区域数量: {len(motion_areas)}\n")
            for i, (x, y, w, h) in enumerate(motion_areas):
                f.write(f"运动区域 {i+1}: 位置({x}, {y}), 大小({w}x{h})\n")
            f.write(f"\n摔倒风险状态: {'高风险' if fall_risk else '正常'}\n")
            f.write(f"累计摔倒次数: {self.fall_count}\n")
            f.write("\n情绪统计:\n")
            for emotion, count in self.emotion_stats.items():
                if count > 0:
                    f.write(f"  {emotion}: {count}\n")
        
        print(f"截图已保存: {filename}")
        print(f"检测信息已保存: {info_filename}")
    
    def run(self):
        """运行摔倒检测系统"""
        print("=== 摔倒检测系统启动 ===")
        print(f"视频源: {self.droidcam_url}")
        print("功能:人脸检测 + 情绪识别 + 动作检测 + 摔倒警报")
        print("🚨 摔倒模拟:竖起大拇指或左右晃手触发警报并自动截图")
        print("按 SPACE 切换检测模式")
        print("按 'q' 退出程序")
        print("按 's' 保存普通截图")
        print("========================")
        
        current_fps = None
        reconnect_attempts = 0
        max_reconnect_attempts = 5
        
        while True:
            ret, frame = self.cap.read()
            if not ret:
                print("警告:无法读取 DroidCam 视频流")
                reconnect_attempts += 1
                if reconnect_attempts > max_reconnect_attempts:
                    print(f"错误:连续 {max_reconnect_attempts} 次无法连接,程序退出")
                    break
                print(f"尝试重新连接 ({reconnect_attempts}/{max_reconnect_attempts})...")
                self.cap.release()
                self.cap = cv2.VideoCapture(self.droidcam_url)
                if not self.cap.isOpened():
                    print("重新连接失败,继续尝试...")
                    time.sleep(1)
                    continue
                print("重新连接成功!")
                reconnect_attempts = 0
                continue
            
            reconnect_attempts = 0
            frame = cv2.flip(frame, 1)
            
            faces = []
            face_emotions = []
            motion_areas = []
            fall_risk = False
            thumbs_positions = []
            wave_positions = []
            
            current_mode = self.detection_modes[self.current_mode]
            
            if current_mode in ['All Detection', 'Face Only']:
                faces = self.detect_faces(frame)
                frame, face_emotions = self.draw_face_and_emotion(frame, faces)
            
            if current_mode in ['All Detection', 'Fall Detection Only']:
                motion_areas, fall_risk, thumbs_positions, motion_mask, wave_positions = self.detect_motion_and_fall(frame)
                frame = self.draw_motion_and_fall(frame, motion_areas, fall_risk, thumbs_positions, wave_positions)
            
            fps = self.calculate_fps()
            if fps is not None:
                current_fps = fps
            
            frame = self.add_info_display(frame, faces, face_emotions, motion_areas, fall_risk, current_fps)
            cv2.imshow('Fall Detection System', frame)
            
            key = cv2.waitKey(1) & 0xFF
            if key == ord('q'):
                print("正在退出程序...")
                break
            elif key == ord('s'):
                self.save_screenshot(frame, faces, face_emotions, motion_areas, fall_risk)
            elif key == ord(' '):
                self.current_mode = (self.current_mode + 1) % len(self.detection_modes)
                print(f"切换到模式: {self.detection_modes[self.current_mode]}")
        
        self.cleanup()
    
    def cleanup(self):
        """清理资源"""
        self.cap.release()
        cv2.destroyAllWindows()
        print("视频流已关闭")
        print(f"本次会话总共检测到 {self.total_faces_detected} 个人脸")
        print(f"摔倒警报触发次数: {self.fall_count}")
        if self.fall_count > 0:
            print("⚠️ 请检查 D:/demoimg/emergency/ 文件夹中的紧急截图")
        print("程序已退出")

if __name__ == "__main__":
    detector = FallDetectionSystem()
    detector.run()
© 版权声明
THE END
喜欢就支持一下吧
点赞13赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容