击剑动作识别程序 - 基于MediaPipe和KNN的击剑动作识别系统
击剑动作识别程序 - 基于MediaPipe和KNN的击剑动作识别系统
本程序使用MediaPipe和KNN算法实现击剑动作识别,可以识别击剑运动中的多个动作,如热身、击打、持剑姿势、敬礼、弓步等。程序支持视频文件输入,并可对识别结果进行可视化展示。
程序功能:
- 使用MediaPipe库进行人体姿势检测,识别人体关键点的位置信息。
- 基于关键点信息计算各个部位的角度,用于动作识别。
- 使用KNN算法对各个动作进行分类识别。
- 支持视频文件输入,并可对识别结果进行可视化展示。
使用说明:
- 运行程序,选择输入视频文件,支持格式:*.mp4, *.avi。
- 可通过滑动条调整距离阈值和邻居数量,用于调节KNN算法的识别精度。
- 点击“运行”按钮开始处理视频文件。
- 视频处理过程中,左上角显示当前帧的动作类型,绿色方块代表执剑手的关键点位置。
- 可以按“q”键退出程序。
程序代码:
import cv2
import mediapipe as mp
import math
import numpy as np
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
#一些全局变量
label = [' ']#识别输出标签
poses = ''#视频输出标签
i = 0
t = 30 #后处理帧数
posess_a = ''
posess_b = ''
version = 'V7.0'
a=1.2#辅助拟合精度
distance_threshold = 121 # 设置KNN距离阈值
neighbors = 2 #邻居
input_path = ''
# 初始化MediaPipe的人体姿势模型
mp_drawing = mp.solutions.drawing_utils
mp_pose = mp.solutions.pose
# # 打开输入视频文件
# cap = cv2.VideoCapture('5.mp4')
import tkinter as tk
from tkinter import filedialog
# 修改distance_threshold
def set_distance_threshold(value):
global distance_threshold
distance_threshold = int(value)
# 创建窗口
root = tk.Tk()
root.withdraw()
# 显示使用说明信息
info = f"欢迎使用击剑动作识别程序{version}\n作者:冯楠20201888\n指导教师:朱勇\n请选择输入视频文件\n支持格式:*.mp4, *.avi\n点击确认选择文件"
tk.messagebox.showinfo('使用说明', info)
# 定义选择文件函数
def select_file():
global input_path
root3 = tk.Tk()
root3.title('选择文件')
input_path = filedialog.askopenfilename(title='选择输入视频文件', filetypes=[('视频文件', '*.mp4;*.avi'), ('所有文件', '*.*')])
root3.destroy()
if not input_path:
tk.messagebox.showerror('错误', '未选择输入视频文件!')
root3.destroy()
return
# 更新标签显示选择的文件路径
file_label.config(text=input_path)
def closeit():
root2.destroy()
# 创建窗口2
root2 = tk.Tk()
root2.title('选择文件,调整参数')
root2.geometry('400x600')
# 创建标题
file_titles = tk.Label(root2, text='击剑动作识别程序', font=('宋体', 18))
file_titles.pack(pady=20)
# 创建标签
file_label = tk.Label(root2, text='未选择文件')
file_label.pack(pady=20)
file_label.config(bg='#e9ccd3')
select_button = tk.Button(root2, text='选择文件', command=select_file)
select_button.pack(pady=10)
# 创建标题
file_titles = tk.Label(root2, text='参数调整', font=('微软雅黑', 13))
file_titles.pack(pady=20)
# 创建滑动条
scale = tk.Scale(root2, from_=0, to=200, orient=tk.HORIZONTAL, length=150, label='距离阈值', command=set_distance_threshold)
scale.set(distance_threshold)
scale.pack()
def set_neighbors(val):
global neighbors
neighbors = int(val)
# 创建滑动条
scale = tk.Scale(root2, from_=1, to=10, orient=tk.HORIZONTAL, length=150, label='邻居数量', command=set_neighbors)
scale.set(neighbors)
scale.pack()
# 创建按钮
run_button = tk.Button(root2, text='运行', command=closeit)
run_button.pack(pady=20)
# 创建标签
file_name = tk.Label(root2, text=f"作者:冯楠2020188 物联网2班\n指导教师:朱勇\n版本:{version}", font=("Arial", 14))
file_name.pack(pady=20)
# 关闭窗口
root.destroy()
# 运行窗口
root2.attributes('-topmost', True)#窗口在最前
root2.mainloop()
# 打开输入视频文件
print('打开输入视频文件')
cap = cv2.VideoCapture(input_path)
# 获取输入视频的帧率和分辨率
fps = int(cap.get(cv2.CAP_PROP_FPS))
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 创建输出视频文件
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(f"{input_path}_OUT.mp4", fourcc, fps, (width, height))
# 加载动作数据集
# 数据清洗
# 将每个小数据集中偏离平均值超过2倍标准差的样本删除
print('数据清洗')
# 将数据集合并为一个大的数据集
# 训练KNN分类器
root = tk.Tk()
root.withdraw()
info1 = '左上角为当前动作,绿色方块代表执剑手位置\n点击确认开始\n点击右上角x退出程序\n在视频处理过程中可以按q退出'
tk.messagebox.showinfo('即将开始', info1)
root.destroy()
# 处理视频文件中的每一帧
with mp_pose.Pose(min_detection_confidence=0.5, min_tracking_confidence=0.3) as pose:
while cap.isOpened():
# 读取一帧
ret, frame = cap.read()
# if ret is None:
# continue
if not ret:
break
# 将帧转换为RGB格式
image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 处理人体姿势检测
results = pose.process(image)
# 判断是否检测到人体
if results.pose_landmarks:
# 绘制人体骨架
mp_drawing.draw_landmarks(
frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)
# 计算腿与右手的角度
# 获取左肩、左肘和左手腕
# 获取左臀、左膝和左踝
# 获取右臀、右膝和右踝
# 获取右肩、右肘和右手腕
# 获取左肩、左肘和左手腕 new
#获取左髋、左膝和左踝关键点的信息
# 获取左髋、左膝和左踝的角度
# 获取腰部、左髋和左肩的角度
# 获取右髋、右膝和右踝关键点的信息
# 获取右髋、右膝和右踝的角度
# 获取腰部、右髋和右肩的角度
# 获取鼻子、右肩和右手腕关键点的信息
# 获取鼻子、右肩和右手腕的角度
# 将数据输入KNN分类器进行预测
distances, indices = knn.kneighbors([[angle, angle1, angle_dl, angle_dr, angle_tr, angle_tr, angle_tl,
angle_lka, angle_hls, angle_rka, angle_hrs, angle_nwr]])
if distances[0][0] > distance_threshold:
label = ['']
confidence = 0
else:
label = knn.predict([[angle, angle1, angle_dl, angle_dr, angle_tr, angle_tr, angle_tl, angle_lka,
angle_hls, angle_rka, angle_hrs, angle_nwr]])
confidence = 1 - distances[0][0] / distance_threshold
# label = knn.predict([[angle, angle1, angle_dl, angle_dr, angle_tr, angle_tr, angle_tl,angle_lka,angle_hls,angle_rka,angle_hrs,angle_nwr]])
print([angle, angle1, angle_dl, angle_dr, angle_tr, angle_tr, angle_tl, angle_lka, angle_hls, angle_rka,
angle_hrs, angle_nwr])
posess_b = poses
if label[0] == 'warm-up':
i = t
poses = 'warm-up'
elif label[0] == 'hit':
i = t
if (angle > -20 * a and angle <= 5 * a) and ((angle_dr > 130.5 / a and angle_dr <= 155 * a) or (
angle_dr > 205 / a and angle_dr <= 212 * a)) and (
(angle_tr > 239 / a and angle_tr <= 260 * a) or (angle_tr > 147 / a and angle_tr <= 170 * a)):
poses = "shi zhan poses"
else:
poses = 'hit'
elif label[0] == 'SHIZHAN POSE':
i = t
poses = 'shi zhan poses'
elif label[0] == 'respect':
i = t
if (angle > -20 * a and angle <= 5 * a) and ((angle_dr > 130.5 / a and angle_dr <= 155 * a) or (
angle_dr > 205 / a and angle_dr <= 212 * a)) and (
(angle_tr > 239 / a and angle_tr <= 260 * a) or (angle_tr > 147 / a and angle_tr <= 170 * a)):
poses = "shi zhan poses"
else:
poses = 'respect'
elif label[0] == 'gongbu':
poses = 'gongbu'
i = t
else:
if i != 0:
peses = poses
i = i - 1
else:
poses = 'N/A'
i = t
# 绘制头部关键点
nose = results.pose_landmarks.landmark[mp_pose.PoseLandmark.NOSE]
x, y = int(nose.x * width), int(nose.y * height)
cv2.circle(frame, (x, y), 20, (220, 200, 100), -1)
# 绘制执剑关键点
zhijian = results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_THUMB]
x, y = int(zhijian.x * width), int(zhijian.y * height)
cv2.rectangle(frame, (x - 20, y - 20), (x + 20, y + 20), (0, 255, 0), -1)
# 显示可信度
if confidence > 0:
cv2.putText(frame, "Current frame action:"+label[0] + " " + str(round(confidence, 2)), (5, 100), cv2.FONT_HERSHEY_SIMPLEX, 1,
(5, 200, 0), 2)
else:
cv2.putText(frame, "Current frame action:"+"N/A", (5, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (5, 200, 0), 2)
if (possess_b != poses):
print('dou')
else:
possess_a = poses
# 在输出图片上显示角度值和动作类型
cv2.putText(frame, "Angle: {:.2f}".format(angle), (5, 210),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.putText(frame, "Angle_rka: {:.2f}".format(angle_rka), (5, 225),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (100, 15, 255), 2)
cv2.putText(frame, "Angle_nwr: {:.2f}".format(angle_nwr), (5, 240),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 100, 255), 2)
cv2.putText(frame, "angle_lka: {:.2f}".format(angle_lka), (5, 255),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.putText(frame, "angle_tl: {:.2f}".format(angle_tl), (5, 270),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 150, 255), 2)
cv2.putText(frame, "angle_hrs: {:.2f}".format(angle_hrs), (5, 285),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (100, 0, 255), 2)
cv2.putText(frame, "Current action:"+possess_a, (5, 60),
cv2.FONT_HERSHEY_SIMPLEX, 1.1, (2, 2, 255), 2)
else:
# 如果未检测到人体,则跳过本帧处理
cv2.putText(frame, "No body detected", (5, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
# 将帧写入输出视频文件
out.write(frame)
# 显示当前帧的结果
cv2.imshow('MediaPipe Pose Detection press q exit', frame)
# 检测是否按下q键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 显示使用说明信息
root = tk.Tk()
root.withdraw()
info = f"视频已输出至{input_path}_OUT.mp4"
tk.messagebox.showinfo('处理完成', info)
# 释放资源
cap.release()
out.release()
cv2.destroyAllWindows()
未来改进的思路和方法,不分段内容:1. 更多的动作类型:可以增加更多的动作类型,如防守、进攻等,增加识别的多样性和准确性。
2. 更多的数据集:可以增加更多的数据集,包括不同击剑选手、不同击剑场地等,以增加模型的鲁棒性和泛化能力。
3. 模型优化:可以尝试使用更先进的模型,如深度学习模型,以提高识别的准确性和效率。
4. 结合实时数据:可以结合实时数据,如击剑选手的心率、呼吸等数据,以更全面地评估击剑选手的状态和表现。
5. 结合VR技术:可以结合虚拟现实技术,为击剑选手提供更真实的击剑体验和训练环境,以提高训练效果和竞技水平。
程序使用截图:
[插入程序使用截图]
程序代码下载:
[插入程序代码下载链接]
程序注意事项:
- 请确保已安装程序所需的库,如MediaPipe、cv2、tkinter、numpy、pandas、sklearn等。
- 程序使用KNN算法进行动作识别,需要根据具体的数据集调整距离阈值和邻居数量。
- 程序支持视频文件输入,请选择正确的视频文件格式。
版权声明:
本程序仅供学习交流使用,版权归原作者所有。
联系方式:
如有任何疑问,请随时联系作者。
感谢使用!
原文地址: https://www.cveoy.top/t/topic/gRd4 著作权归作者所有。请勿转载和采集!