【CV】计算两个向量的夹角,并使用 OpenCV 可视化弧线

时间:2024-01-23 08:36:50


背景

基于人体/动物,骨骼点数据,计算关节角度

1. 原理

计算两个向量的夹角,我们已三个点为例,BA 向量和BC向量,求 B 的角度。若为四个点,延长交叉即可。

【CV】计算两个向量的夹角,并使用 OpenCV 可视化弧线_python

2. 效果

效果图如下

【CV】计算两个向量的夹角,并使用 OpenCV 可视化弧线_opencv_02

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. 应用

骨骼点角度计算和可视化

【CV】计算两个向量的夹角,并使用 OpenCV 可视化弧线_人工智能_03