168 lines
5.3 KiB
Python
168 lines
5.3 KiB
Python
import cv2
|
||
import numpy as np
|
||
import time
|
||
import os
|
||
|
||
# 导入我们的3D手部检测模块
|
||
from src.hand_detection_3d import load_mediapipe_model, process_frame_3d
|
||
|
||
# 尝试导入 Picamera2,如果不可用则使用标准OpenCV
|
||
try:
|
||
from picamera2 import Picamera2
|
||
PICAMERA_AVAILABLE = True
|
||
except ImportError:
|
||
PICAMERA_AVAILABLE = False
|
||
|
||
def process_camera_3d(camera_id=0, output_path=None, use_picamera=True):
|
||
"""
|
||
使用摄像头进行3D手部检测 - 简化版本
|
||
|
||
参数:
|
||
camera_id: 摄像头ID (默认为0)
|
||
output_path: 输出视频文件路径 (可选)
|
||
use_picamera: 是否优先使用Picamera2 (树莓派摄像头)
|
||
"""
|
||
# 加载MediaPipe手部检测模型
|
||
hands_model = load_mediapipe_model(max_num_hands=1)
|
||
|
||
# 初始化摄像头
|
||
cap = None
|
||
picam2 = None
|
||
|
||
if use_picamera and PICAMERA_AVAILABLE:
|
||
# 使用 Picamera2 (树莓派摄像头)
|
||
try:
|
||
print("正在初始化 Picamera2...")
|
||
picam2 = Picamera2()
|
||
video_config = picam2.create_video_configuration(main={"size": (640, 480)})
|
||
picam2.configure(video_config)
|
||
picam2.start()
|
||
print("✅ Picamera2 启动成功")
|
||
frame_width, frame_height = 640, 480
|
||
except Exception as e:
|
||
print(f"❌ Picamera2 启动失败: {e}")
|
||
print("⚠️ 回退到 OpenCV VideoCapture")
|
||
picam2 = None
|
||
use_picamera = False
|
||
|
||
if not use_picamera or not PICAMERA_AVAILABLE:
|
||
# 使用标准 OpenCV VideoCapture
|
||
cap = cv2.VideoCapture(camera_id)
|
||
if not cap.isOpened():
|
||
print(f"无法打开摄像头 ID: {camera_id}")
|
||
return
|
||
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
||
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
||
|
||
fps = 30
|
||
|
||
# 创建视频写入器(如果需要保存)
|
||
video_writer = None
|
||
if output_path:
|
||
fourcc = cv2.VideoWriter_fourcc(*'XVID')
|
||
video_writer = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height))
|
||
|
||
# 前一帧的手部数据
|
||
prev_hand_data = None
|
||
|
||
# 初始化性能计数器
|
||
frame_count = 0
|
||
start_time = time.time()
|
||
|
||
print("控制说明:")
|
||
print("- 移动手部显示控制信号")
|
||
print("- 拇指和食指捏合显示抓取状态")
|
||
print("- 按 Ctrl+C 退出")
|
||
|
||
try:
|
||
while True:
|
||
# 读取一帧
|
||
ret = False
|
||
frame = None
|
||
|
||
if picam2:
|
||
try:
|
||
frame = picam2.capture_array()
|
||
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
|
||
ret = True
|
||
except Exception as e:
|
||
print(f"Picamera2 读取帧失败: {e}")
|
||
break
|
||
else:
|
||
ret, frame = cap.read()
|
||
|
||
if not ret or frame is None:
|
||
break
|
||
|
||
frame_count += 1
|
||
|
||
# 处理帧并获取三轴控制信号
|
||
control_signal, prev_hand_data = process_frame_3d(frame, hands_model, prev_hand_data)
|
||
|
||
# 显示控制信号
|
||
if control_signal and frame_count % 30 == 0: # 每秒显示一次
|
||
print_control_signal(control_signal)
|
||
|
||
# 写入输出视频
|
||
if video_writer:
|
||
video_writer.write(frame)
|
||
|
||
except KeyboardInterrupt:
|
||
print("\n收到中断信号,正在退出...")
|
||
except Exception as e:
|
||
print(f"程序运行时出错: {e}")
|
||
finally:
|
||
print("正在清理资源...")
|
||
|
||
# 释放资源
|
||
if picam2:
|
||
try:
|
||
picam2.stop()
|
||
picam2.close()
|
||
except:
|
||
pass
|
||
|
||
if cap:
|
||
try:
|
||
cap.release()
|
||
except:
|
||
pass
|
||
|
||
if video_writer:
|
||
try:
|
||
video_writer.release()
|
||
print(f"✅ 视频已保存: {output_path}")
|
||
except:
|
||
pass
|
||
|
||
# 显示统计信息
|
||
elapsed_time = time.time() - start_time
|
||
if frame_count > 0:
|
||
avg_fps = frame_count / elapsed_time
|
||
print(f"📊 处理了 {frame_count} 帧,平均FPS: {avg_fps:.1f}")
|
||
|
||
print("✅ 程序已退出")
|
||
|
||
def print_control_signal(control_signal):
|
||
"""显示手部控制信号信息"""
|
||
print(f"手部控制信号:")
|
||
print(f" X轴角度: {control_signal['x_angle']:.1f}°")
|
||
print(f" Y轴角度: {control_signal['y_angle']:.1f}°")
|
||
print(f" Z轴角度: {control_signal['z_angle']:.1f}°")
|
||
print(f" 抓取状态: {'抓取' if control_signal['grip'] == 1 else '释放'}")
|
||
print(f" 动作: {control_signal['action']}")
|
||
print("-------------------------------")
|
||
|
||
if __name__ == "__main__":
|
||
import argparse
|
||
|
||
parser = argparse.ArgumentParser(description='简化版3D手部检测')
|
||
parser.add_argument('--camera-id', '-i', type=int, default=0, help='摄像头ID (默认为0)')
|
||
parser.add_argument('--output', '-o', help='输出视频文件路径')
|
||
|
||
args = parser.parse_args()
|
||
|
||
process_camera_3d(
|
||
camera_id=args.camera_id,
|
||
output_path=args.output
|
||
) |