1、配置环境:
pip install mediepipe
pip install opencv-python
2、原理介绍:
将Mediapipe用于行为检测是比较复杂的一件事;如果这样做,那么行为检测的精度就完全取决于Mediapipe关键点的检测精度。
于是可以根据下图中人的关节夹角来对人的位姿进行检测。
3、身体关键点识别
import cv2
import mediapipe as mp
mp_drawing = mp.solutions.drawing_utils
mp_drawing_styles = mp.solutions.drawing_styles
mp_holistic = mp.solutions.holistic
# 读取摄像头
cap = cv2.VideoCapture(0)
with mp_holistic.Holistic(
min_detection_confidence=0.5,
min_tracking_confidence=0.5) as holistic:
while cap.isOpened():
success, image = cap.read()
if not success:
print("Ignoring empty camera frame.")
#如果加载视频,请使用“break”而不是“continue”。
continue
#为了提高性能,可以选择将图像标记为不可写入
#通过引用传递
image.flags.writeable = False
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = holistic.process(image)
#在图像上绘制地标注释
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
mp_drawing.draw_landmarks(
image,
results.face_landmarks,
mp_holistic.FACEMESH_CONTOURS,
landmark_drawing_spec=None,
connection_drawing_spec=mp_drawing_styles
.get_default_face_mesh_contours_style())
mp_drawing.draw_landmarks(
image,
results.pose_landmarks,
mp_holistic.POSE_CONNECTIONS,
landmark_drawing_spec=mp_drawing_styles
.get_default_pose_landmarks_style())
#水平翻转图像以显示自拍视图。
cv2.imshow('MediaPipe Holistic', cv2.flip(image, 1))
if cv2.waitKey(5) & 0xFF == 27:
break
cap.release()
4、创建工具类
import cv2
import mediapipe as mp
import math
class PoseDetector():
'''
人体姿势检测类
'''
def __init__(self,
static_image_mode=False,
upper_body_only=False,
smooth_landmarks=True,
min_detection_confidence=0.5,
min_tracking_confidence=0.5):
'''
初始化
:param static_image_mode: 是否是静态图片,默认为否
:param upper_body_only: 是否是上半身,默认为否
:param smooth_landmarks: 设置为True减少抖动
:param min_detection_confidence:人员检测模型的最小置信度值,默认为0.5
:param min_tracking_confidence:姿势可信标记的最小置信度值,默认为0.5
'''
self.static_image_mode = static_image_mode
self.upper_body_only = upper_body_only
self.smooth_landmarks = smooth_landmarks
self.min_detection_confidence = min_detection_confidence
self.min_tracking_confidence = min_tracking_confidence
# 创建一个Pose对象用于检测人体姿势
self.pose = mp.solutions.pose.Pose(self.static_image_mode, self.upper_body_only, False, self.smooth_landmarks,self.min_detection_confidence, self.min_tracking_confidence)
def find_pose(self, img, draw=True):
'''
检测姿势方法
:param img: 一帧图像
:param draw: 是否画出人体姿势节点和连接图
:return: 处理过的图像
'''
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# pose.process(imgRGB) 会识别这帧图片中的人体姿势数据,保存到self.results中
self.results = self.pose.process(imgRGB)
if self.results.pose_landmarks:
if draw:
mp.solutions.drawing_utils.draw_landmarks(img, self.results.pose_landmarks,
mp.solutions.pose.POSE_CONNECTIONS)
return img
def find_positions(self, img):
'''
获取人体姿势数据
:param img: 一帧图像
:param draw: 是否画出人体姿势节点和连接图
:return: 人体姿势数据列表
'''
# 人体姿势数据列表,每个成员由3个数字组成:id, x, y
# id代表人体的某个关节点,x和y代表坐标位置数据
self.lmslist = []
if self.results.pose_landmarks:
for id, lm in enumerate(self.results.pose_landmarks.landmark):
h, w, c = img.shape
cx, cy = int(lm.x * w), int(lm.y * h)
self.lmslist.append([id, cx, cy])
return self.lmslist
def find_angle(self, img, p1, p2, p3, draw=True):
'''
获取人体姿势中3个点p1-p2-p3的角度
:param img: 一帧图像
:param p1: 第1个点
:param p2: 第2个点
:param p3: 第3个点
:param draw: 是否画出3个点的连接图
:return: 角度
'''
x1, y1 = self.lmslist[p1][1], self.lmslist[p1][2]
x2, y2 = self.lmslist[p2][1], self.lmslist[p2][2]
x3, y3 = self.lmslist[p3][1], self.lmslist[p3][2]
# 使用三角函数公式获取3个点p1-p2-p3,以p2为角的角度值,0-180度之间
angle = int(math.degrees(math.atan2(y1 - y2, x1 - x2) - math.atan2(y3 - y2, x3 - x2)))
if angle < 0:
angle = angle + 360
if angle > 180:
angle = 360 - angle
if draw:
cv2.circle(img, (x1, y1), 20, (0, 255, 255), cv2.FILLED)
cv2.circle(img, (x2, y2), 30, (255, 0, 255), cv2.FILLED)
cv2.circle(img, (x3, y3), 20, (0, 255, 255), cv2.FILLED)
cv2.line(img, (x1, y1), (x2, y2), (255, 255, 255, 3))
cv2.line(img, (x2, y2), (x3, y3), (255, 255, 255, 3))
cv2.putText(img, str(angle), (x2 - 50, y2 + 50), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 255), 2)
return angle
5、俯卧撑检测
# 导入opencv工具包
import cv2
# 导入numpy
import numpy as np
# 导入姿势识别器
from poseutil import PoseDetector
# 打开视频文件
cap = cv2.VideoCapture('videos/pushup.mp4')
# 姿势识别器
detector = PoseDetector()
# 方向与个数
dir = 0 # 0为下,1为上
count = 0
# 视频宽度高度
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('videos/pushupoutput.mp4', fourcc, 30.0, (width, height))
while True:
# 读取摄像头,img为每帧图片
success, img = cap.read()
if success:
h, w, c = img.shape
# 识别姿势
img = detector.find_pose(img, draw=True)
# 获取姿势数据
positions = detector.find_positions(img)
if positions:
# 获取俯卧撑的角度
angle1 = detector.find_angle(img, 12, 24, 26)
angle2 = detector.find_angle(img, 12, 14, 16)
# 进度条长度
bar = np.interp(angle2, (45, 150), (w // 2 - 100, w // 2 + 100))
cv2.rectangle(img, (w // 2 - 100, h - 150), (int(bar), h - 100), (0, 255, 0), cv2.FILLED)
# 角度小于50度认为撑下
if angle2 <= 50 and angle1 >= 165 and angle1 <= 175:
if dir == 0:
count = count + 0.5
dir = 1
# 角度大于125度认为撑起
if angle2 >= 125 and angle1 >= 165 and angle1 <= 175:
if dir == 1:
count = count + 0.5
dir = 0
cv2.putText(img, str(int(count)), (w // 2, h // 2), cv2.FONT_HERSHEY_SIMPLEX, 10, (255, 255, 255), 20, cv2.LINE_AA)
# 打开一个Image窗口显示视频图片
cv2.imshow('Image', img)
# 录制视频
out.write(img)
else:
# 视频结束退出
break
# 如果按下q键,程序退出
key = cv2.waitKey(1)
if key == ord('q'):
break
# 关闭视频保存器
out.release()
# 关闭摄像头
cap.release()
# 关闭程序窗口
cv2.destroyAllWindows()
6、引体向上
# 导入opencv工具包
import cv2
# 导入numpy
import numpy as np
# 导入姿势识别器
from poseutil import PoseDetector
# 打开视频文件
cap = cv2.VideoCapture('videos/pushup.mp4')
# 姿势识别器
detector = PoseDetector()
# 方向与个数
dir = 0 # 0为下,1为上
count = 0
# 视频宽度高度
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('videos/pushupoutput.mp4', fourcc, 30.0, (width, height))
while True:
# 读取摄像头,img为每帧图片
success, img = cap.read()
if success:
h, w, c = img.shape
# 识别姿势
img = detector.find_pose(img, draw=True)
# 获取姿势数据
positions = detector.find_positions(img)
if positions:
# 获取俯卧撑的角度
angle1 = detector.find_angle(img, 12, 24, 26)
angle2 = detector.find_angle(img, 12, 14, 16)
# 进度条长度
bar = np.interp(angle2, (45, 150), (w // 2 - 100, w // 2 + 100))
cv2.rectangle(img, (w // 2 - 100, h - 150), (int(bar), h - 100), (0, 255, 0), cv2.FILLED)
# 角度小于50度认为撑下
if angle2 <= 50 and angle1 >= 165 and angle1 <= 175:
if dir == 0:
count = count + 0.5
dir = 1
# 角度大于125度认为撑起
if angle2 >= 125 and angle1 >= 165 and angle1 <= 175:
if dir == 1:
count = count + 0.5
dir = 0
cv2.putText(img, str(int(count)), (w // 2, h // 2), cv2.FONT_HERSHEY_SIMPLEX, 10, (255, 255, 255), 20, cv2.LINE_AA)
# 打开一个Image窗口显示视频图片
cv2.imshow('Image', img)
# 录制视频
out.write(img)
else:
# 视频结束退出
break
# 如果按下q键,程序退出
key = cv2.waitKey(1)
if key == ord('q'):
break
# 关闭视频保存器
out.release()
# 关闭摄像头
cap.release()
# 关闭程序窗口
cv2.destroyAllWindows()
7、仰卧起坐
# 仰卧启坐
# 导入opencv工具包
import cv2
# 导入numpy
import numpy as np
# 导入姿势识别器
from poseutil import PoseDetector
# 打开视频文件
cap = cv2.VideoCapture(0)
# 姿势识别器
detector = PoseDetector()
# 方向与个数
dir = 0 # 0为躺下,1为坐起
count = 0
while True:
# 读取摄像头,img为每帧图片
success, img = cap.read()
if success:
h, w, c = img.shape
# 识别姿势
img = detector.find_pose(img, draw=True)
# 获取姿势数据
positions = detector.find_positions(img)
if positions:
# 获取的角度
angle = detector.find_angle(img, 11, 23, 25)
# 进度条长度
bar = np.interp(angle, (50, 130), (w // 2 - 100, w // 2 + 100))
cv2.rectangle(img, (w // 2 - 100, h - 150), (int(bar), h - 100), (0, 255, 0), cv2.FILLED)
# 角度小于55度认为坐起
if angle <= 55:
if dir == 0:
count = count + 0.5
dir = 1
# 角度大于120度认为躺下
if angle >= 120:
if dir == 1:
count = count + 0.5
dir = 0
cv2.putText(img, str(int(count)), (w // 2, h // 2), cv2.FONT_HERSHEY_SIMPLEX, 10, (255, 255, 255), 20,
cv2.LINE_AA)
# 打开一个Image窗口显示视频图片
cv2.imshow('Image', img)
else:
# 视频结束退出
break
# 如果按下q键,程序退出
key = cv2.waitKey(1)
if key == ord('q'):
break
# 关闭摄像头
cap.release()
# 关闭程序窗口
cv2.destroyAllWindows()
8、深蹲检测
# 导入opencv工具包
import cv2
# 导入numpy
import numpy as np
# 导入姿势识别器
from poseutil import PoseDetector
# 打开视频文件
cap = cv2.VideoCapture('videos/pushup.mp4')
# 姿势识别器
detector = PoseDetector()
# 方向与个数
dir = 0 # 0为下,1为上
count = 0
# 视频宽度高度
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('videos/pushupoutput.mp4', fourcc, 30.0, (width, height))
while True:
# 读取摄像头,img为每帧图片
success, img = cap.read()
if success:
h, w, c = img.shape
# 识别姿势
img = detector.find_pose(img, draw=True)
# 获取姿势数据
positions = detector.find_positions(img)
if positions:
# 获取俯卧撑的角度
angle1 = detector.find_angle(img, 12, 24, 26)
angle2 = detector.find_angle(img, 12, 14, 16)
# 进度条长度
bar = np.interp(angle2, (45, 150), (w // 2 - 100, w // 2 + 100))
cv2.rectangle(img, (w // 2 - 100, h - 150), (int(bar), h - 100), (0, 255, 0), cv2.FILLED)
# 角度小于50度认为撑下
if angle2 <= 50 and angle1 >= 165 and angle1 <= 175:
if dir == 0:
count = count + 0.5
dir = 1
# 角度大于125度认为撑起
if angle2 >= 125 and angle1 >= 165 and angle1 <= 175:
if dir == 1:
count = count + 0.5
dir = 0
cv2.putText(img, str(int(count)), (w // 2, h // 2), cv2.FONT_HERSHEY_SIMPLEX, 10, (255, 255, 255), 20, cv2.LINE_AA)
# 打开一个Image窗口显示视频图片
cv2.imshow('Image', img)
# 录制视频
out.write(img)
else:
# 视频结束退出
break
# 如果按下q键,程序退出
key = cv2.waitKey(1)
if key == ord('q'):
break
# 关闭视频保存器
out.release()
# 关闭摄像头
cap.release()
# 关闭程序窗口
cv2.destroyAllWindows()
9、叉腰检测
# 叉腰
# 导入opencv工具包
import cv2
# 导入numpy
import numpy as np
# 导入姿势识别器
from poseutil import PoseDetector
# 打开视频文件
cap = cv2.VideoCapture(0)
# 姿势识别器
detector = PoseDetector()
# 方向与个数
dir = 0 # 0为站立,1为蹲下
count = 0
while True:
# 读取摄像头,img为每帧图片
success, img = cap.read()
if success:
h, w, c = img.shape
# 识别姿势
img = detector.find_pose(img, draw=True)
# 获取姿势数据
positions = detector.find_positions(img)
if positions:
# 获取的角度
angle = detector.find_angle(img, 12, 14, 16)
angle2 = detector.find_angle(img, 11, 13, 15)
# 进度条长度
bar = np.interp(angle, (50, 170), (w // 2 - 100, w // 2 + 100))
cv2.rectangle(img, (w // 2 - 100, h - 150), (int(bar), h - 100), (0, 255, 0), cv2.FILLED)
# 角度()度认为叉腰
if angle <= 105 and angle2 <= 105:
if dir == 0:
count = count + 0.5
dir = 1
# 角度()度认为站立
if angle >= 160 and angle2 >= 160:
if dir == 1:
count = count + 0.5
dir = 0
cv2.putText(img, str(int(count)), (w // 2, h // 2), cv2.FONT_HERSHEY_SIMPLEX, 10, (255, 255, 255), 20,
cv2.LINE_AA)
# 打开一个Image窗口显示视频图片
cv2.imshow('Image', img)
else:
# 视频结束退出
break
# 如果按下q键,程序退出
key = cv2.waitKey(1)
if key == ord('q'):
break
# 关闭摄像头
cap.release()
# 关闭程序窗口
cv2.destroyAllWindows()
10、下蹲
# 下蹲
# 导入opencv工具包
import cv2
# 导入numpy
import numpy as np
# 导入姿势识别器
from poseutil import PoseDetector
# 打开视频文件
cap = cv2.VideoCapture(0)
# 姿势识别器
detector = PoseDetector()
# 方向与个数
dir = 0
count = 0
while True:
# 读取摄像头,img为每帧图片
success, img = cap.read()
if success:
h, w, c = img.shape
# 识别姿势
img = detector.find_pose(img, draw=True)
# 获取姿势数据
positions = detector.find_positions(img)
if positions:
# 获取的角度
angle = detector.find_angle(img, 14, 12, 11)
angle2 = detector.find_angle(img, 13, 11, 12)
angle3 = detector.find_angle(img, 12, 14, 16)
angle4 = detector.find_angle(img, 11, 13, 15)
# 进度条长度
bar = np.interp(angle, (50, 170), (w // 2 - 100, w // 2 + 100))
cv2.rectangle(img, (w // 2 - 100, h - 150), (int(bar), h - 100), (0, 255, 0), cv2.FILLED)
# 角度()认为平抬手
if angle >= 165 and angle2 >= 165:
if dir == 0:
count = count + 0.5
dir = 1
# 角度()认为站立
if angle <= 165 and angle2 <= 165:
if dir == 1:
count = count + 0.5
dir = 0
cv2.putText(img, str(int(count)), (w // 2, h // 2), cv2.FONT_HERSHEY_SIMPLEX, 10, (255, 255, 255), 20,
cv2.LINE_AA)
# 打开一个Image窗口显示视频图片
cv2.imshow('Image', img)
else:
# 视频结束退出
break
# 如果按下q键,程序退出
key = cv2.waitKey(1)
if key == ord('q'):
break
# 关闭摄像头
cap.release()
# 关闭程序窗口
cv2.destroyAllWindows()