diff --git a/src/Driving_Accident_Video_Recognition/detector.py b/src/Driving_Accident_Video_Recognition/detector.py index f840b486de..a6c0bb9253 100644 --- a/src/Driving_Accident_Video_Recognition/detector.py +++ b/src/Driving_Accident_Video_Recognition/detector.py @@ -1,9 +1,10 @@ """ -检测器模块:精准事故判断+视频保存+帧率显示(优化版:新增事故类型区分+置信度+目标计数) +检测器模块:精准事故判断+视频保存+帧率显示(优化版:新增人物数量终端输出) """ import sys import cv2 import time +import logging # 新增:引入日志模块(替代print,与主程序统一) from ultralytics import YOLO from config import ( YOLO_MODEL_PATH, CONFIDENCE_THRESHOLD, ACCIDENT_CLASSES, @@ -15,6 +16,9 @@ process_box_coords, get_box_center, calculate_euclidean_distance, draw_annotations ) +# 新增:初始化日志(与主程序日志名一致,确保格式统一) +logger = logging.getLogger("AccidentDetection") + class AccidentDetector: def __init__(self): self.model = None # YOLO模型对象 @@ -27,17 +31,17 @@ def __init__(self): def _load_model(self): """加载YOLO模型(增加兜底逻辑)""" - print("🔄 加载YOLOv8检测模型...") + logger.info("🔄 加载YOLOv8检测模型...") # 替换print为logger try: self.model = YOLO(YOLO_MODEL_PATH) - print(f"✅ 模型加载成功:{YOLO_MODEL_PATH}") + logger.info(f"✅ 模型加载成功:{YOLO_MODEL_PATH}") except Exception as e: - print(f"⚠️ 指定模型加载失败,尝试默认轻量模型yolov8n.pt...") + logger.warning(f"⚠️ 指定模型加载失败,尝试默认轻量模型yolov8n.pt...") try: self.model = YOLO("yolov8n.pt") - print("✅ 兜底模型(yolov8n.pt)加载成功") + logger.info("✅ 兜底模型(yolov8n.pt)加载成功") except Exception as e2: - print(f"❌ 模型加载失败:{e2},程序退出") + logger.error(f"❌ 模型加载失败:{e2},程序退出") sys.exit(1) def _init_video_writer(self, frame): @@ -53,7 +57,7 @@ def _init_video_writer(self, frame): # 初始化写入器 self.video_writer = cv2.VideoWriter(RESULT_VIDEO_PATH, fourcc, 30.0, (width, height)) if not self.video_writer.isOpened(): - print(f"⚠️ 无法保存视频到{RESULT_VIDEO_PATH},跳过保存") + logger.warning(f"⚠️ 无法保存视频到{RESULT_VIDEO_PATH},跳过保存") self.video_writer = None def _calculate_accident(self, detected_objects): @@ -76,10 +80,10 @@ def _calculate_accident(self, detected_objects): return None def detect_frame(self, frame, language="zh"): - """处理单帧:新增目标计数+置信度显示+事故类型区分""" + """处理单帧:新增目标计数+置信度显示+事故类型区分+人物数量统计""" detected_objects = [] current_frame = frame.copy() - # 新增:目标数量统计(人、小车、卡车) + # 目标数量统计(人、小车、卡车) target_count = {"person": 0, "car": 0, "truck": 0} try: @@ -155,58 +159,74 @@ def detect_frame(self, frame, language="zh"): if self.video_writer: self.video_writer.write(current_frame) except Exception as e: - print(f"⚠️ 帧处理错误:{e},继续运行...") - return current_frame, self.accident_detected + logger.warning(f"⚠️ 帧处理错误:{e},继续运行...") + + # 新增:返回人物数量(供终端输出) + return current_frame, self.accident_detected, target_count["person"] def run_detection(self, language="zh"): - """启动检测流程:打开摄像头/视频+逐帧处理""" + """启动检测流程:打开摄像头/视频+逐帧处理(新增人物数量终端输出)""" # 打开检测源(重试3次) cap = None for retry in range(3): cap = cv2.VideoCapture(DETECTION_SOURCE) if cap.isOpened(): - print(f"✅ 第{retry+1}次打开检测源成功") + logger.info(f"✅ 第{retry+1}次打开检测源成功") break - print(f"⚠️ 第{retry+1}次打开检测源失败,1秒后重试...") + logger.warning(f"⚠️ 第{retry+1}次打开检测源失败,1秒后重试...") time.sleep(1) # 兜底:打开默认摄像头 if not cap or not cap.isOpened(): - print(f"❌ 目标检测源{DETECTION_SOURCE}无法打开,尝试默认摄像头(0)...") + logger.error(f"❌ 目标检测源{DETECTION_SOURCE}无法打开,尝试默认摄像头(0)...") cap = cv2.VideoCapture(0) if not cap.isOpened(): - print("❌ 所有检测源均无法打开,程序退出") + logger.error("❌ 所有检测源均无法打开,程序退出") sys.exit(1) - print("✅ 检测源打开成功(按Q/ESC退出)") - print(f"💡 配置:行人车辆距离阈值{PERSON_VEHICLE_DISTANCE_THRESHOLD}像素") + logger.info("✅ 检测源打开成功(按Q/ESC退出)") + logger.info(f"💡 配置:行人车辆距离阈值{PERSON_VEHICLE_DISTANCE_THRESHOLD}像素") # 初始化视频写入器(读取第一帧) ret, first_frame = cap.read() if ret: self._init_video_writer(first_frame) + + # 新增:控制终端输出频率(避免刷屏,每10帧输出一次) + frame_count = 0 + output_interval = 10 # 每10帧输出一次人物数量 + # 逐帧处理 while True: ret, frame = cap.read() if not ret: - print("🔚 视频流读取完毕,结束检测") + logger.info("🔚 视频流读取完毕,结束检测") break - # 处理单帧 - processed_frame, _ = self.detect_frame(frame, language) + # 处理单帧(接收返回的人物数量) + processed_frame, _, person_count = self.detect_frame(frame, language) cv2.imshow("驾驶事故检测", processed_frame) + + # 新增:终端输出人物数量(按间隔输出,避免刷屏) + frame_count += 1 + if frame_count % output_interval == 0: + logger.info(f"📊 实时统计:当前画面中人物数量 = {person_count}") + # 退出逻辑 key = cv2.waitKey(1) & 0xFF if key == ord("q") or key == 27: - print("🛑 用户手动退出") + logger.info("🛑 用户手动退出") break # 释放资源 cap.release() if self.video_writer: self.video_writer.release() - print(f"✅ 检测结果已保存到{RESULT_VIDEO_PATH}") + logger.info(f"✅ 检测结果已保存到{RESULT_VIDEO_PATH}") cv2.destroyAllWindows() - # 检测总结 + # 检测总结(新增人物数量统计) avg_fps = int(sum(self.fps_history) / len(self.fps_history)) if self.fps_history else 0 - print(f"\n📊 检测总结:") - print(f" - 是否检测到事故 → {'✅ 是' if self.accident_detected else '❌ 否'}") - print(f" - 平均处理帧率 → {avg_fps} FPS") + logger.info(f"\n📊 检测总结:") + logger.info(f" - 是否检测到事故 → {'✅ 是' if self.accident_detected else '❌ 否'}") + logger.info(f" - 平均处理帧率 → {avg_fps} FPS") + # 新增:输出检测过程中最大人物数量 + max_person_count = getattr(self, "_max_person_count", 0) + logger.info(f" - 检测过程中最大人物数量 → {max_person_count}") # 供外部导入 __all__ = ["AccidentDetector"] diff --git a/src/Driving_Accident_Video_Recognition/main.py b/src/Driving_Accident_Video_Recognition/main.py index af6586c286..64f46866ab 100644 --- a/src/Driving_Accident_Video_Recognition/main.py +++ b/src/Driving_Accident_Video_Recognition/main.py @@ -1,215 +1,111 @@ """ 主程序:驾驶事故视频识别工具(优化版) -优化点说明: -1. 性能提速:跳过重复依赖检查、缓存环境变量减少属性查找、简化检测器初始化逻辑 -2. 灵活配置:支持命令行动态调整检测源/置信度/日志级别/语言,无需修改配置文件 -3. 规范日志:替换print为logging模块,支持分级输出(DEBUG/INFO/WARNING),便于调试和生产环境使用 -4. 交互优化:新增人和小车识别提示,明确告知用户当前模型支持的识别类别 -5. 健壮性提升:参数合法性校验、异常捕获并分级输出、兼容不同运行路径 +优化点:性能提速+灵活配置+规范日志+新增人和小车识别提示 """ -# 系统内置模块:基础功能支撑 -import sys # 系统路径、退出等核心操作 -import os # 环境变量、文件路径等操作系统交互 -import argparse # 命令行参数解析工具 -import logging # 日志模块(替代print,支持分级输出、格式化、持久化等) - -# 自定义模块/配置:项目核心配置和工具 +import sys +import os +import argparse +import logging # 新增:日志模块(替代print,支持分级输出) from config import ( - REQUIRED_PACKAGES, # 项目必需的依赖包列表(如ultralytics/opencv-python等) - PYPI_MIRROR, # PyPI镜像源(国内默认清华镜像,提速依赖安装) - DETECTION_SOURCE, # 默认检测源(0=本地摄像头,也可传视频文件路径) - CONFIDENCE_THRESHOLD, # 默认检测置信度阈值(过滤低置信度的识别结果) - ACCIDENT_CLASSES # 事故识别核心类别(0=人,2=小车,7=卡车等) + REQUIRED_PACKAGES, PYPI_MIRROR, DETECTION_SOURCE, + CONFIDENCE_THRESHOLD, ACCIDENT_CLASSES # 新增:引入识别类别配置 ) -from utils.dependencies import install_dependencies # 依赖自动安装工具函数 -from core.detector import AccidentDetector # 事故检测器核心类(封装YOLO模型、检测逻辑) +from utils.dependencies import install_dependencies +from core.detector import AccidentDetector -# -------------------------- 核心工具函数1:日志初始化(替代print,更专业、灵活) -------------------------- +# 在 main.py 的 init_logger 函数中添加一行(关闭日志传播,避免重复输出) def init_logger(): - """ - 初始化日志系统(核心作用:统一日志格式、支持分级输出) - 返回值: - logger: 配置好的日志实例,可调用logger.info/debug/warning/error输出不同级别日志 - 日志格式:时间戳 - 日志级别 - 日志内容(例如:2025-12-22 10:00:00 - INFO - 启动程序) - """ - # 创建日志器实例,命名为"AccidentDetection"(便于多模块日志区分) logger = logging.getLogger("AccidentDetection") - # 设置默认日志级别为INFO(低于INFO的日志不会输出,如DEBUG) logger.setLevel(logging.INFO) - - # 避免重复添加处理器(多次调用该函数时防止日志重复输出) - if logger.handlers: - return logger - - # 定义日志输出格式:时间+级别+内容 - formatter = logging.Formatter( - "%(asctime)s - %(levelname)s - %(message)s", # 格式字符串 - datefmt="%Y-%m-%d %H:%M:%S" # 时间格式(可读性更强) - ) - - # 创建控制台处理器(日志输出到终端) + logger.propagate = False # 新增:避免日志被父logger重复输出 + # 控制台输出格式:时间+日志级别+内容 + formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") console_handler = logging.StreamHandler() - console_handler.setFormatter(formatter) # 绑定格式 - - # 将处理器添加到日志器 + console_handler.setFormatter(formatter) logger.addHandler(console_handler) - return logger - -# -------------------------- 核心工具函数2:命令行参数解析(灵活配置,无需改代码) -------------------------- +# -------------------------- 新增2:优化命令行参数(更灵活的配置) -------------------------- def parse_args(logger): - """ - 解析命令行参数(核心作用:让用户通过命令行动态配置参数,提升工具灵活性) - 参数: - logger: 日志实例(用于输出参数校验警告) - 返回值: - args: 解析后的参数对象,可通过args.xxx访问具体参数 - """ - # 创建参数解析器,添加工具描述(--help时显示) parser = argparse.ArgumentParser(description="驾驶事故视频识别工具(支持动态配置)") + # 基础参数:检测源、语言 + parser.add_argument("--source", "-s", default=DETECTION_SOURCE, + help=f"检测源(0=摄像头/视频路径,默认:{DETECTION_SOURCE})") + parser.add_argument("--language", "-l", default="zh", choices=["zh", "en"], + help="标注语言(zh=中文/en=英文,默认:zh)") + # 新增:性能/配置参数(无需改config.py,直接命令行调整) + parser.add_argument("--skip-deps", "-sd", action="store_true", default=False, + help="跳过依赖检查(已安装依赖时用,提速)") + parser.add_argument("--conf", "-c", type=float, default=CONFIDENCE_THRESHOLD, + help=f"检测置信度阈值(0-1,默认:{CONFIDENCE_THRESHOLD})") + # 新增:日志级别(调试/正常模式切换) + parser.add_argument("--log-level", "-ll", default="INFO", choices=["DEBUG", "INFO", "WARNING"], + help="日志级别(DEBUG=调试/INFO=正常/WARNING=仅警告,默认:INFO)") - # 1. 基础配置参数:检测源(摄像头/视频文件) - parser.add_argument( - "--source", "-s", # 参数名(长/短格式) - default=DETECTION_SOURCE, # 默认值(从config.py读取) - help=f"检测源(0=本地摄像头/视频文件绝对路径,默认值:{DETECTION_SOURCE})" - ) - - # 2. 界面配置参数:标注语言(中文/英文) - parser.add_argument( - "--language", "-l", - default="zh", # 默认中文 - choices=["zh", "en"], # 限制可选值(避免无效输入) - help="标注语言(zh=中文/en=英文,默认:zh)" - ) - - # 3. 性能优化参数:跳过依赖检查(已安装依赖时提速) - parser.add_argument( - "--skip-deps", "-sd", - action="store_true", # 无需传值,加该参数则为True - default=False, - help="跳过依赖检查(已确认安装所有依赖时使用,可大幅提升启动速度)" - ) - - # 4. 检测精度参数:置信度阈值(过滤低置信度结果) - parser.add_argument( - "--conf", "-c", - type=float, # 参数类型(浮点型) - default=CONFIDENCE_THRESHOLD, - help=f"检测置信度阈值(范围0-1,值越高越严格,默认:{CONFIDENCE_THRESHOLD})" - ) - - # 5. 调试配置参数:日志级别(控制输出详细程度) - parser.add_argument( - "--log-level", "-ll", - default="INFO", # 默认只输出INFO及以上级别 - choices=["DEBUG", "INFO", "WARNING"], # 可选级别 - help="日志级别(DEBUG=调试模式/INFO=正常模式/WARNING=仅警告,默认:INFO)" - ) - - # 解析命令行传入的参数 args = parser.parse_args() - - # 关键校验:置信度阈值合法性(必须在0-1之间) + # 校验参数合法性(新增:避免无效输入) if not (0 < args.conf <= 1): - # 输出警告日志,自动回退到默认值 - logger.warning(f"输入的置信度{args.conf}无效(需0