multi_group_system.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. """
  2. 多组摄像头系统管理器
  3. 管理多组(全景+球机)摄像头的并行独立运行
  4. """
  5. import os
  6. import sys
  7. import time
  8. import logging
  9. import threading
  10. from typing import List, Optional, Dict, Any
  11. from concurrent.futures import ThreadPoolExecutor
  12. # 添加项目路径
  13. sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
  14. from config import (
  15. CAMERA_GROUPS, get_enabled_groups,
  16. SDK_PATH, DETECTION_CONFIG, COORDINATOR_CONFIG,
  17. CALIBRATION_CONFIG, SYSTEM_CONFIG
  18. )
  19. from dahua_sdk import DahuaSDK
  20. from panorama_camera import ObjectDetector
  21. from ocr_recognizer import NumberDetector
  22. from camera_group import CameraGroup
  23. logger = logging.getLogger(__name__)
  24. class MultiGroupSystem:
  25. """
  26. 多组摄像头系统管理器
  27. 管理多组(全景+球机)摄像头:
  28. - 共享 SDK 实例(大华SDK只需初始化一次)
  29. - 共享检测器(YOLO模型只加载一次)
  30. - 各组并行独立运行检测和抓拍
  31. """
  32. def __init__(self, config_override: dict = None):
  33. """
  34. 初始化多组系统
  35. Args:
  36. config_override: 配置覆盖
  37. """
  38. self.config = config_override or {}
  39. # 共享组件
  40. self.sdk: Optional[DahuaSDK] = None
  41. self.detector: Optional[ObjectDetector] = None
  42. self.number_detector: Optional[NumberDetector] = None
  43. # 摄像头组列表
  44. self.groups: List[CameraGroup] = []
  45. # 运行状态
  46. self.running = False
  47. self.initialized = False
  48. logger.info("[MultiGroupSystem] 多组摄像头系统实例创建")
  49. def initialize(self, skip_calibration: bool = False) -> bool:
  50. """
  51. 初始化系统(SDK + 检测器 + 所有组)
  52. Args:
  53. skip_calibration: 是否跳过校准
  54. Returns:
  55. 是否成功
  56. """
  57. logger.info("=" * 60)
  58. logger.info("[MultiGroupSystem] 开始初始化多组摄像头系统...")
  59. logger.info("=" * 60)
  60. # 1. 初始化检测器(先于SDK,避免内存冲突)
  61. try:
  62. self.detector = ObjectDetector(
  63. model_path=self.config.get('model_path', DETECTION_CONFIG.get('model_path')),
  64. use_gpu=self.config.get('use_gpu', DETECTION_CONFIG.get('use_gpu', True)),
  65. model_size=self.config.get('model_size', 'n'),
  66. model_type=self.config.get('model_type', DETECTION_CONFIG.get('model_type', 'auto'))
  67. )
  68. logger.info("[MultiGroupSystem] 检测器初始化成功")
  69. except Exception as e:
  70. logger.warning(f"[MultiGroupSystem] 检测器初始化失败: {e}")
  71. # 2. 初始化编号检测器
  72. try:
  73. from config import OCR_CONFIG
  74. ocr_config = {
  75. 'api_host': self.config.get('ocr_host', OCR_CONFIG['api_host']),
  76. 'api_port': self.config.get('ocr_port', OCR_CONFIG['api_port']),
  77. 'model': self.config.get('ocr_model', OCR_CONFIG['model']),
  78. }
  79. self.number_detector = NumberDetector(use_api=True, ocr_config=ocr_config)
  80. logger.info("[MultiGroupSystem] 编号检测器初始化成功")
  81. except Exception as e:
  82. logger.warning(f"[MultiGroupSystem] 编号检测器初始化失败: {e}")
  83. # 3. 初始化SDK
  84. sdk_path = os.path.join(
  85. self.config.get('sdk_path', SDK_PATH['lib_path']),
  86. self.config.get('netsdk', SDK_PATH['netsdk'])
  87. )
  88. try:
  89. self.sdk = DahuaSDK(sdk_path)
  90. if not self.sdk.init():
  91. logger.error("[MultiGroupSystem] SDK初始化失败")
  92. return False
  93. logger.info("[MultiGroupSystem] SDK初始化成功")
  94. except Exception as e:
  95. logger.error(f"[MultiGroupSystem] SDK加载失败: {e}")
  96. return False
  97. # 4. 获取启用的摄像头组配置
  98. enabled_groups = get_enabled_groups()
  99. if not enabled_groups:
  100. logger.error("[MultiGroupSystem] 没有启用的摄像头组!请在 config/camera.py 中配置 CAMERA_GROUPS")
  101. return False
  102. logger.info(f"[MultiGroupSystem] 检测到 {len(enabled_groups)} 个启用的摄像头组")
  103. # 5. 初始化各组
  104. shared_config = {
  105. 'coordinator_config': COORDINATOR_CONFIG,
  106. 'calibration_config': CALIBRATION_CONFIG,
  107. }
  108. success_count = 0
  109. for group_config in enabled_groups:
  110. group_id = group_config.get('group_id', 'unknown')
  111. logger.info(f"[MultiGroupSystem] 初始化组: {group_id}")
  112. group = CameraGroup(
  113. group_config=group_config,
  114. sdk=self.sdk,
  115. detector=self.detector,
  116. number_detector=self.number_detector,
  117. shared_config=shared_config
  118. )
  119. if group.initialize(skip_calibration=skip_calibration):
  120. self.groups.append(group)
  121. success_count += 1
  122. logger.info(f"[MultiGroupSystem] 组 {group_id} 初始化成功")
  123. else:
  124. logger.error(f"[MultiGroupSystem] 组 {group_id} 初始化失败")
  125. if success_count == 0:
  126. logger.error("[MultiGroupSystem] 所有组初始化失败!")
  127. return False
  128. logger.info(f"[MultiGroupSystem] 成功初始化 {success_count}/{len(enabled_groups)} 个组")
  129. self.initialized = True
  130. return True
  131. def start(self) -> bool:
  132. """
  133. 并行启动所有组
  134. Returns:
  135. 是否成功
  136. """
  137. if not self.initialized:
  138. logger.error("[MultiGroupSystem] 系统未初始化,无法启动")
  139. return False
  140. logger.info(f"[MultiGroupSystem] 启动 {len(self.groups)} 个摄像头组...")
  141. success_count = 0
  142. for group in self.groups:
  143. if group.start():
  144. success_count += 1
  145. else:
  146. logger.error(f"[MultiGroupSystem] 组 {group.group_id} 启动失败")
  147. if success_count == 0:
  148. logger.error("[MultiGroupSystem] 所有组启动失败!")
  149. return False
  150. self.running = True
  151. logger.info(f"[MultiGroupSystem] 成功启动 {success_count}/{len(self.groups)} 个组")
  152. # 启动定时校准
  153. interval_hours = CALIBRATION_CONFIG.get('interval', 24 * 60 * 60) // 3600
  154. daily_time = CALIBRATION_CONFIG.get('daily_calibration_time', '08:00')
  155. for group in self.groups:
  156. group.start_scheduled_calibration(
  157. interval_hours=interval_hours,
  158. daily_time=daily_time
  159. )
  160. return True
  161. def stop(self):
  162. """停止所有组"""
  163. logger.info("[MultiGroupSystem] 停止所有摄像头组...")
  164. self.running = False
  165. for group in self.groups:
  166. try:
  167. group.stop()
  168. except Exception as e:
  169. logger.error(f"[MultiGroupSystem] 停止组 {group.group_id} 失败: {e}")
  170. # 清理SDK
  171. if self.sdk:
  172. try:
  173. self.sdk.cleanup()
  174. except Exception as e:
  175. logger.error(f"[MultiGroupSystem] SDK清理失败: {e}")
  176. logger.info("[MultiGroupSystem] 系统已停止")
  177. def get_status(self) -> Dict[str, Any]:
  178. """获取系统状态"""
  179. return {
  180. 'running': self.running,
  181. 'initialized': self.initialized,
  182. 'total_groups': len(self.groups),
  183. 'groups': [group.get_status() for group in self.groups],
  184. }
  185. def calibrate_all(self, force: bool = False) -> bool:
  186. """
  187. 校准所有组
  188. Args:
  189. force: 是否强制重新校准
  190. Returns:
  191. 是否全部成功
  192. """
  193. logger.info(f"[MultiGroupSystem] 开始校准所有组...")
  194. success_count = 0
  195. for group in self.groups:
  196. logger.info(f"[MultiGroupSystem] 校准组: {group.group_id}")
  197. if group._auto_calibrate(force=force):
  198. success_count += 1
  199. logger.info(f"[MultiGroupSystem] 校准完成: {success_count}/{len(self.groups)} 组成功")
  200. return success_count == len(self.groups)
  201. def wait(self):
  202. """等待系统运行(阻塞)"""
  203. try:
  204. while self.running:
  205. time.sleep(1)
  206. except KeyboardInterrupt:
  207. logger.info("[MultiGroupSystem] 收到中断信号")
  208. self.stop()
  209. def main():
  210. """多组系统主入口"""
  211. import argparse
  212. parser = argparse.ArgumentParser(description='多组摄像头联动抓拍系统')
  213. parser.add_argument('--skip-calibration', action='store_true', help='跳过自动校准')
  214. parser.add_argument('--calibrate-only', action='store_true', help='仅执行校准后退出')
  215. args = parser.parse_args()
  216. # 设置日志
  217. logging.basicConfig(
  218. level=logging.INFO,
  219. format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
  220. )
  221. # 创建系统
  222. system = MultiGroupSystem()
  223. # 初始化
  224. if not system.initialize(skip_calibration=args.skip_calibration):
  225. print("系统初始化失败!")
  226. return 1
  227. # 仅校准模式
  228. if args.calibrate_only:
  229. print("校准完成,退出。")
  230. system.stop()
  231. return 0
  232. # 启动
  233. if not system.start():
  234. print("系统启动失败!")
  235. return 1
  236. print(f"\n多组摄像头系统已启动 ({len(system.groups)} 个组)")
  237. print("按 Ctrl+C 停止\n")
  238. # 等待
  239. try:
  240. system.wait()
  241. except KeyboardInterrupt:
  242. pass
  243. return 0
  244. if __name__ == '__main__':
  245. sys.exit(main())