背景
基于人体/动物,骨骼点数据,计算关节角度
1. 原理
计算两个向量的夹角,我们已三个点为例,BA 向量和BC向量,求 B 的角度。若为四个点,延长交叉即可。
2. 效果
效果图如下
3. 核心代码
def compute_vector_angle(a, b, c):
"""
计算两个向量 ba 和 bc 向量的夹角
@params a , 点 a
@params b , 点 b
@params c , 点 c
"""
# 我这里为了好理解使用三个点数据,也可以用四个点
triangle_data = [a, b, c]
# 向量夹角
ba_angle = math.atan2(a[1] - b[1], a[0] - b[0]) * 180 / math.pi
bc_angle = math.atan2(c[1] - b[1], c[0] - b[0]) * 180 / math.pi
b_angle = 0
if ba_angle * bc_angle >= 0:
# 第1,4象限
b_angle = abs(ba_angle - bc_angle)
else:
# 第2,3象限
b_angle = abs(ba_angle) + abs(bc_angle)
if b_angle > 180:
b_angle = 360 - b_angle
print("\nba_angle:", ba_angle, "bc_angle:", bc_angle, "angle:", b_angle)
start_angle = ba_angle
end_angle = bc_angle
return start_angle, end_angle, b_angle, triangle_data
4. 可视化
图像可视化,坐标和圆的坐标系如下,
- 坐标系,相反
- 圆坐标系,逆时针,如图所示
5. 全部代码
实现 Python 代码,可直接执行,C++ 同理实现
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Create : 2024/01/09 15:03:33
@Author : Yuan Mingzhuo
"""
import numpy as np
import cv2
import math
def compute_vector_angle(a, b, c):
"""
计算两个向量 ba 和 bc 向量的夹角
@params a , 点 a
@params b , 点 b
@params c , 点 c
"""
# 我这里为了好理解使用三个点数据,也可以用四个点
triangle_data = [a, b, c]
# 向量夹角
ba_angle = math.atan2(a[1] - b[1], a[0] - b[0]) * 180 / math.pi
bc_angle = math.atan2(c[1] - b[1], c[0] - b[0]) * 180 / math.pi
b_angle = 0
if ba_angle * bc_angle >= 0:
# 第1,4象限
b_angle = abs(ba_angle - bc_angle)
else:
# 第2,3象限
b_angle = abs(ba_angle) + abs(bc_angle)
if b_angle > 180:
b_angle = 360 - b_angle
print("\nba_angle:", ba_angle, "bc_angle:", bc_angle, "angle:", b_angle)
start_angle = ba_angle
end_angle = bc_angle
return start_angle, end_angle, b_angle, triangle_data
def draw_image_vector_angle(image, triangle_data, start_angle, end_angle, angle):
"""
可视化显示
@params image , 图
@params triangle_data , 向量点数据
@start_angle , ba 向量角度
@end_angle , bc 向量角度
@angle , ba 与 bc 向量夹角
"""
# 点
point_a = (int(triangle_data[0][0]), int(triangle_data[0][1]))
point_b = (int(triangle_data[1][0]), int(triangle_data[1][1]))
point_c = (int(triangle_data[2][0]), int(triangle_data[2][1]))
for point in triangle_data:
cv2.circle(
img=image,
center=(int(point[0]), int(point[1])),
radius=2,
color=(255, 255, 255),
thickness=1,
lineType=cv2.LINE_AA,
)
# 线
lines = [(point_b, point_a), (point_b, point_c), (point_a, point_c)]
for line in lines:
cv2.line(
img=image,
pt1=line[0],
pt2=line[1],
color=(0, 0, 255),
thickness=1,
lineType=cv2.LINE_AA,
)
# 弧线
startAngle = start_angle
endAngle = end_angle
if start_angle * end_angle < 0:
angle_data = abs(start_angle) + abs(end_angle)
if angle_data > 180:
# 取反弧度结束角
if end_angle > 0:
endAngle = end_angle - 360
else:
endAngle = end_angle + 360
print("DRAW : ", "startAngle", startAngle, "endAngle", endAngle)
cv2.ellipse(
img=image,
center=point_b,
axes=(16, 16),
angle=360,
startAngle=startAngle,
endAngle=endAngle,
color=(255, 255, 255),
thickness=1,
lineType=cv2.LINE_AA,
)
# 角 B
angle_b_point = [
int(((point_a[0] + point_b[0] + point_c[0]) / 3)),
int(((point_a[1] + point_b[1] + point_c[1]) / 3)),
]
cv2.putText(
img=image,
text=f"{int(abs(angle))}",
org=angle_b_point,
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
fontScale=0.3,
color=(200, 200, 200),
thickness=1,
lineType=cv2.LINE_AA,
)
return image
if __name__ == "__main__":
"""
可视化测试
"""
# 测试点
a, b, c = (70, 100), (100, 150), (170, 70)
# 图像
image = np.full((300, 300, 3), 0, dtype=np.uint8)
# 计算
start_angle, end_angle, angle, triangle_data = compute_vector_angle(a, b, c)
# 绘制
image = draw_image_vector_angle(
image=image,
triangle_data=triangle_data,
start_angle=start_angle,
end_angle=end_angle,
angle=angle,
)
# 显示
cv2.imshow("test", image)
cv2.waitKey(3000)
6. 应用
骨骼点角度计算和可视化