camera_scanner.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # 文件名: camera_scanner.py
  4. import json
  5. import logging
  6. import sys
  7. import time
  8. from pathlib import Path
  9. import cv2
  10. # 导入ConfigManager类
  11. from src.utils.config_manager import ConfigManager
  12. # 添加项目根目录到系统路径,以便导入src中的模块
  13. project_root = Path(__file__).parent.parent
  14. sys.path.append(str(project_root))
  15. # 设置日志记录
  16. logging.basicConfig(
  17. level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
  18. )
  19. logger = logging.getLogger("CameraScanner")
  20. def get_camera_capabilities(cam):
  21. """
  22. 获取摄像头的参数和能力.
  23. """
  24. capabilities = {}
  25. # 获取可用的分辨率
  26. standard_resolutions = [
  27. (640, 480), # VGA
  28. (800, 600), # SVGA
  29. (1024, 768), # XGA
  30. (1280, 720), # HD
  31. (1280, 960), # 4:3 HD
  32. (1920, 1080), # Full HD
  33. (2560, 1440), # QHD
  34. (3840, 2160), # 4K UHD
  35. ]
  36. supported_resolutions = []
  37. original_width = int(cam.get(cv2.CAP_PROP_FRAME_WIDTH))
  38. original_height = int(cam.get(cv2.CAP_PROP_FRAME_HEIGHT))
  39. # 记录原始分辨率
  40. capabilities["default_resolution"] = (original_width, original_height)
  41. # 测试标准分辨率
  42. for width, height in standard_resolutions:
  43. cam.set(cv2.CAP_PROP_FRAME_WIDTH, width)
  44. cam.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
  45. actual_width = int(cam.get(cv2.CAP_PROP_FRAME_WIDTH))
  46. actual_height = int(cam.get(cv2.CAP_PROP_FRAME_HEIGHT))
  47. # 如果设置成功(实际分辨率与请求的相同)
  48. if actual_width == width and actual_height == height:
  49. supported_resolutions.append((width, height))
  50. # 恢复原始分辨率
  51. cam.set(cv2.CAP_PROP_FRAME_WIDTH, original_width)
  52. cam.set(cv2.CAP_PROP_FRAME_HEIGHT, original_height)
  53. capabilities["supported_resolutions"] = supported_resolutions
  54. # 获取帧率
  55. fps = int(cam.get(cv2.CAP_PROP_FPS))
  56. capabilities["fps"] = fps if fps > 0 else 30 # 默认为30fps
  57. # 获取后端名称
  58. backend_name = cam.getBackendName()
  59. capabilities["backend"] = backend_name
  60. return capabilities
  61. def detect_cameras():
  62. """
  63. 检测并列出所有可用摄像头.
  64. """
  65. print("\n===== 摄像头设备检测 =====\n")
  66. # 获取ConfigManager实例
  67. config_manager = ConfigManager.get_instance()
  68. # 获取当前相机配置
  69. current_camera_config = config_manager.get_config("CAMERA", {})
  70. logger.info(f"当前相机配置: {current_camera_config}")
  71. # 显示当前配置
  72. if current_camera_config:
  73. print("当前摄像头配置:")
  74. print(f" - 索引: {current_camera_config.get('camera_index', '未设置')}")
  75. print(
  76. f" - 分辨率: {current_camera_config.get('frame_width', '未设置')}x{current_camera_config.get('frame_height', '未设置')}"
  77. )
  78. print(
  79. f" - 分辨率: {current_camera_config.get('frame_width', '未设置')}x{current_camera_config.get('frame_height', '未设置')}"
  80. )
  81. print(f" - 帧率: {current_camera_config.get('fps', '未设置')}")
  82. print(f" - VL模型: {current_camera_config.get('models', '未设置')}")
  83. print("")
  84. # 存储找到的设备
  85. camera_devices = []
  86. # 尝试打开多个摄像头索引
  87. max_cameras_to_check = 10 # 最多检查10个摄像头索引
  88. for i in range(max_cameras_to_check):
  89. try:
  90. # 尝试打开摄像头
  91. cap = cv2.VideoCapture(i)
  92. if cap.isOpened():
  93. # 获取摄像头信息
  94. device_name = f"Camera {i}"
  95. try:
  96. # 在某些系统上可能可以获取设备名称
  97. device_name = cap.getBackendName() + f" Camera {i}"
  98. except Exception as e:
  99. logger.warning(f"获取设备{i}名称失败: {e}")
  100. # 读取一帧以确保摄像头正常工作
  101. ret, frame = cap.read()
  102. if not ret:
  103. print(f"设备 {i}: 打开成功但无法读取画面,跳过")
  104. cap.release()
  105. continue
  106. # 获取摄像头能力
  107. capabilities = get_camera_capabilities(cap)
  108. # 打印设备信息
  109. width, height = capabilities["default_resolution"]
  110. resolutions_str = ", ".join(
  111. [f"{w}x{h}" for w, h in capabilities["supported_resolutions"]]
  112. )
  113. print(f"设备 {i}: {device_name}")
  114. print(f" - 默认分辨率: {width}x{height}")
  115. print(f" - 支持分辨率: {resolutions_str}")
  116. print(f" - 帧率: {capabilities['fps']}")
  117. print(f" - 后端: {capabilities['backend']}")
  118. # 标记当前配置使用的摄像头
  119. current_index = current_camera_config.get("camera_index")
  120. if current_index == i:
  121. print("当前配置使用的摄像头")
  122. # 添加到设备列表
  123. camera_devices.append(
  124. {"index": i, "name": device_name, "capabilities": capabilities}
  125. )
  126. # 测试摄像头功能
  127. print(f"正在测试设备 {i} 的摄像头功能...")
  128. try:
  129. # 快速测试 - 读取几帧
  130. test_frames = 0
  131. start_time = time.time()
  132. while test_frames < 10 and time.time() - start_time < 2:
  133. ret, frame = cap.read()
  134. if ret:
  135. test_frames += 1
  136. else:
  137. break
  138. if test_frames >= 5:
  139. print(f" ✓ 摄像头功能正常 (测试读取 {test_frames} 帧)")
  140. else:
  141. print(f" ⚠ 摄像头功能可能异常 (仅读取 {test_frames} 帧)")
  142. except Exception as e:
  143. print(f" ✗ 摄像头功能测试失败: {e}")
  144. # 询问是否显示预览
  145. print(f"是否显示设备 {i} 的预览画面?(y/n,默认n): ", end="")
  146. show_preview = input().strip().lower()
  147. if show_preview == "y":
  148. print(f"正在显示设备 {i} 的预览画面,按 'q' 键或等待3秒继续...")
  149. preview_start = time.time()
  150. while time.time() - preview_start < 3:
  151. ret, frame = cap.read()
  152. if ret:
  153. cv2.imshow(f"Camera {i} Preview", frame)
  154. if cv2.waitKey(1) & 0xFF == ord("q"):
  155. break
  156. cv2.destroyAllWindows()
  157. cap.release()
  158. else:
  159. # 如果连续两个索引无法打开摄像头,则认为没有更多摄像头了
  160. consecutive_failures = 0
  161. for j in range(i, i + 2):
  162. temp_cap = cv2.VideoCapture(j)
  163. if not temp_cap.isOpened():
  164. consecutive_failures += 1
  165. temp_cap.release()
  166. if consecutive_failures >= 2 and i > 0:
  167. break
  168. except Exception as e:
  169. print(f"检测设备 {i} 时出错: {e}")
  170. # 总结找到的设备
  171. print("\n===== 设备总结 =====\n")
  172. if not camera_devices:
  173. print("未找到可用的摄像头设备!")
  174. return None
  175. print(f"找到 {len(camera_devices)} 个摄像头设备:")
  176. for device in camera_devices:
  177. width, height = device["capabilities"]["default_resolution"]
  178. print(f" - 设备 {device['index']}: {device['name']}")
  179. print(f" 分辨率: {width}x{height}")
  180. # 推荐最佳设备
  181. print("\n===== 推荐设备 =====\n")
  182. # 首选高清摄像头,其次是分辨率最高的
  183. recommended_camera = None
  184. highest_resolution = 0
  185. for device in camera_devices:
  186. width, height = device["capabilities"]["default_resolution"]
  187. resolution = width * height
  188. # 如果是HD或以上分辨率
  189. if width >= 1280 and height >= 720:
  190. if resolution > highest_resolution:
  191. highest_resolution = resolution
  192. recommended_camera = device
  193. elif recommended_camera is None or resolution > highest_resolution:
  194. highest_resolution = resolution
  195. recommended_camera = device
  196. # 打印推荐设备
  197. if recommended_camera:
  198. r_width, r_height = recommended_camera["capabilities"]["default_resolution"]
  199. print(
  200. f"推荐摄像头: 设备 {recommended_camera['index']} "
  201. f"({recommended_camera['name']})"
  202. )
  203. print(f" - 分辨率: {r_width}x{r_height}")
  204. print(f" - 帧率: {recommended_camera['capabilities']['fps']}")
  205. # 从现有配置中获取VL API信息
  206. vl_url = current_camera_config.get(
  207. "Loacl_VL_url", "https://open.bigmodel.cn/api/paas/v4/"
  208. )
  209. vl_api_key = current_camera_config.get("VLapi_key", "你自己的key")
  210. model = current_camera_config.get("models", "glm-4v-plus")
  211. # 生成配置文件示例
  212. print("\n===== 配置文件示例 =====\n")
  213. if recommended_camera:
  214. new_camera_config = {
  215. "camera_index": recommended_camera["index"],
  216. "frame_width": r_width,
  217. "frame_height": r_height,
  218. "fps": recommended_camera["capabilities"]["fps"],
  219. "Local_VL_url": vl_url, # 保留原有值
  220. "VLapi_key": vl_api_key, # 保留原有值
  221. "models": model, # 保留原有值
  222. }
  223. print("推荐的摄像头配置:")
  224. print(json.dumps(new_camera_config, indent=2, ensure_ascii=False))
  225. # 比较配置变化
  226. print("\n===== 配置变化对比 =====\n")
  227. current_index = current_camera_config.get("camera_index")
  228. current_width = current_camera_config.get("frame_width")
  229. current_height = current_camera_config.get("frame_height")
  230. current_fps = current_camera_config.get("fps")
  231. changes = []
  232. if current_index != recommended_camera["index"]:
  233. changes.append(
  234. f"摄像头索引: {current_index} → {recommended_camera['index']}"
  235. )
  236. if current_width != r_width or current_height != r_height:
  237. changes.append(
  238. f"分辨率: {current_width}x{current_height} → {r_width}x{r_height}"
  239. )
  240. if current_fps != recommended_camera["capabilities"]["fps"]:
  241. changes.append(
  242. f"帧率: {current_fps} → {recommended_camera['capabilities']['fps']}"
  243. )
  244. if changes:
  245. print("检测到以下配置变化:")
  246. for change in changes:
  247. print(f" - {change}")
  248. else:
  249. print("推荐配置与当前配置相同,无需更新")
  250. # 询问是否更新配置文件
  251. if changes:
  252. print("\n是否要更新配置文件中的摄像头配置?(y/n): ", end="")
  253. choice = input().strip().lower()
  254. if choice == "y":
  255. try:
  256. # 使用ConfigManager更新配置
  257. success = config_manager.update_config("CAMERA", new_camera_config)
  258. if success:
  259. print("\n✓ 摄像头配置已成功更新到config.json!")
  260. print("\n===== 最新配置 =====\n")
  261. updated_config = config_manager.get_config("CAMERA", {})
  262. print(json.dumps(updated_config, indent=2, ensure_ascii=False))
  263. else:
  264. print("\n✗ 更新摄像头配置失败!")
  265. except Exception as e:
  266. logger.error(f"更新配置时出错: {e}")
  267. print(f"\n✗ 更新配置时出错: {e}")
  268. else:
  269. print("\n配置未更新")
  270. else:
  271. print("未找到推荐的摄像头配置")
  272. return camera_devices
  273. if __name__ == "__main__":
  274. try:
  275. cameras = detect_cameras()
  276. if cameras:
  277. print("\n检测到 {len(cameras)} 个摄像头设备!")
  278. else:
  279. print("\n未检测到可用的摄像头设备!")
  280. except Exception:
  281. logger.error("检测过程中出错: {e}")
  282. print("检测过程中出错: {e}")