人体动作识别:使用KNN分类器识别动作并可视化
import cv2
import math
import numpy as np
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
neighbors = 2 # 邻居
# 加载动作数据集
warmup_df = pd.read_csv('dataset_warm-up.csv')
combat_df = pd.read_csv('dataset_SHIZHAN POSE.csv')
attack_df = pd.read_csv('dataset_hit.csv')
respect_df = pd.read_csv('dataset_respect.csv')
gongbu_df = pd.read_csv('dataset_gongbu.csv')
# 数据清洗
# 将每个小数据集中偏离平均值超过2倍标准差的样本删除
print('数据清洗')
for df in [warmup_df, combat_df, attack_df, respect_df, gongbu_df]:
df.drop(df[(np.abs(df[['angle1', 'angle2', 'angle3', 'angle4', 'angle5', 'angle5_1', 'angle6', 'angle7', 'angle8', 'angle9', 'angle10', 'angle11']] - df[['angle1', 'angle2', 'angle3', 'angle4', 'angle5', 'angle5_1', 'angle6', 'angle7', 'angle8', 'angle9', 'angle10', 'angle11']].mean()) > 2 * df[['angle1', 'angle2', 'angle3', 'angle4', 'angle5', 'angle6', 'angle7', 'angle8', 'angle9', 'angle10', 'angle11']].std()).any(axis=1)].index, inplace=True)
# 将数据集合并为一个大的数据集
data = pd.concat([warmup_df, combat_df, attack_df, respect_df, gongbu_df], ignore_index=True)
# 训练KNN分类器
print('开始训练knn')
knn = KNeighborsClassifier(n_neighbors=neighbors, weights='distance', metric='manhattan')
knn.fit(data[['angle1', 'angle2', 'angle3', 'angle4', 'angle5', 'angle5_1', 'angle6', 'angle7', 'angle8', 'angle9', 'angle10', 'angle11']], data['label'])
# 加载测试视频
cap = cv2.VideoCapture('test.mp4')
# 定义字典,用于将数字标签转换为动作名称
actions = {0: 'warm-up', 1: 'combat', 2: 'attack', 3: 'respect', 4: 'gongbu'}
# 定义字典,用于将动作名称转换为颜色
colors = {'warm-up': (0, 255, 0), 'combat': (255, 0, 0), 'attack': (0, 0, 255), 'respect': (255, 255, 0), 'gongbu': (0, 255, 255)}
# 定义函数,用于绘制骨架
def draw_skeleton(frame, angles, color):
# 定义骨架关键点的坐标
points = [(100, 100),
(100, 50),
(100, 0),
(75, 75),
(50, 50),
(25, 25),
(25, 75),
(0, 50),
(100, 125),
(75, 125),
(50, 125),
(25, 125)]
# 定义骨架关键点的连接方式
connections = [(0, 1),
(1, 2),
(1, 3),
(3, 4),
(4, 5),
(3, 6),
(6, 7),
(1, 8),
(8, 9),
(9, 10),
(10, 11)]
# 绘制骨架
for connection in connections:
start_point = points[connection[0]]
end_point = points[connection[1]]
cv2.line(frame, start_point, end_point, color, 2)
# 绘制关键点
for i, angle in enumerate(angles):
x = int(points[i][0] + 25 * math.cos(math.radians(angle)))
y = int(points[i][1] + 25 * math.sin(math.radians(angle)))
cv2.circle(frame, (x, y), 3, color, -1)
# 定义函数,用于预处理帧图像
def preprocess(frame):
# 将图像转换为灰度图像
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 使用高斯滤波去除噪声
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# 使用Canny边缘检测算法检测边缘
edges = cv2.Canny(blur, 50, 150)
# 使用膨胀操作填充边缘
kernel = np.ones((5, 5), np.uint8)
dilation = cv2.dilate(edges, kernel, iterations=1)
# 返回处理后的图像
return dilation
# 定义函数,用于从帧图像中提取角度特征
def extract_angles(frame, points):
angles = []
for i, connection in enumerate([(0, 1), (1, 2), (1, 3), (3, 4), (4, 5), (3, 6), (6, 7), (1, 8), (8, 9), (9, 10), (10, 11)]):
start_point = points[connection[0]]
end_point = points[connection[1]]
dx = end_point[0] - start_point[0]
dy = end_point[1] - start_point[1]
angle = math.degrees(math.atan2(dy, dx))
angles.append(angle)
return angles
# 定义函数,用于将预测结果可视化
def visualize_prediction(frame, label):
# 获取动作名称和颜色
action = actions[label]
color = colors[action]
# 在帧图像上绘制动作名称和颜色
cv2.putText(frame, action, (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
# 处理视频帧
while True:
# 读取视频帧
ret, frame = cap.read()
if not ret:
break
# 预处理帧图像
processed = preprocess(frame)
# 使用OpenCV的findContours函数查找轮廓
contours, _ = cv2.findContours(processed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 如果没有找到轮廓,则跳过该帧
if len(contours) == 0:
continue
# 找到最大的轮廓
contour = max(contours, key=cv2.contourArea)
# 使用OpenCV的convexHull函数计算凸包
hull = cv2.convexHull(contour)
# 使用OpenCV的approxPolyDP函数近似凸包
approx = cv2.approxPolyDP(hull, 0.001 * cv2.arcLength(hull, True), True)
# 如果近似凸包的顶点数不足12个,则跳过该帧
if len(approx) < 12:
continue
# 将顶点按照x坐标排序
approx = sorted(approx, key=lambda x: x[0][0])
# 提取关键点坐标
points = [(approx[0][0][0], approx[0][0][1]),
(approx[1][0][0], approx[1][0][1]),
(approx[2][0][0], approx[2][0][1]),
(approx[3][0][0], approx[3][0][1]),
(approx[4][0][0], approx[4][0][1]),
(approx[5][0][0], approx[5][0][1]),
(approx[6][0][0], approx[6][0][1]),
(approx[7][0][0], approx[7][0][1]),
(approx[8][0][0], approx[8][0][1]),
(approx[9][0][0], approx[9][0][1]),
(approx[10][0][0], approx[10][0][1]),
(approx[11][0][0], approx[11][0][1])]
# 提取角度特征
angles = extract_angles(frame, points)
# 预测动作
label = knn.predict([angles])[0]
# 可视化预测结果
visualize_prediction(frame, label)
# 绘制骨架
draw_skeleton(frame, angles, colors[actions[label]])
# 显示帧图像
cv2.imshow('frame', frame)
if cv2.waitKey(1) == ord('q'):
break
# 释放视频捕获对象和窗口
cap.release()
cv2.destroyAllWindows()
原文地址: https://www.cveoy.top/t/topic/gQ1e 著作权归作者所有。请勿转载和采集!