如何判断一个角是否大于180度(2)

news2024/11/18 7:52:46

理论计算见上一篇:

如何判断一个角是否大于180度?_kv1830的博客-CSDN博客

此篇为代码实现

一。直接上代码:

import cv2 as cv
import numpy as np
import math


def get_vector(p_from, p_to):
    return p_to[0] - p_from[0], p_to[1] - p_from[1]


def get_unit_vector(v):
    """
    获取单位向量
    """
    x, y = v
    length = (x ** 2 + y ** 2) ** 0.5
    return x / length, y / length


def calc_angle_by_sincos(sin, cos):
    if sin >= 0 and cos >= 0:
        angle = math.asin(sin)
    elif cos < 0:
        angle = math.pi - math.asin(sin)
    elif sin < 0 and cos >= 0:
        angle = math.asin(sin) + math.pi * 2
    else:
        raise ValueError(f'ignore case: sin: {sin}, cos: {cos}')
    angle = angle * 180 / math.pi
    return angle


def calc_angle_by_axis_x(v):
    """
    由向量计算与x轴的夹角,x轴顺时针到向量
    """
    x, y = get_unit_vector(v)
    return calc_angle_by_sincos(y, x)


def calc_angle(p_a, p_b, p_c):
    """
    角点为p_b,由b_a顺时针转到b_c的角
    注:y轴向下,就是顺时针,比如在opencv中。
       y轴向上,就是逆时针。所以顺逆只在一念之间~~
    """
    print(f'p_a: {p_a}, p_b: {p_b}, p_c: {p_c}')
    v_ba = get_vector(p_b, p_a)
    v_bc = get_vector(p_b, p_c)

    v_ba, v_bc = get_unit_vector(v_ba), get_unit_vector(v_bc)
    x1, y1 = v_ba
    x2, y2 = v_bc

    # 求解旋转方程
    a = np.array([[-y1, x1], [x1, y1]], dtype=np.float32)
    b = np.array([x2, y2], dtype=np.float32)
    result_flag, (sin, cos) = cv.solve(a, b)
    sin = sin[0]
    cos = cos[0]
    print(f'sin = {sin}, cos = {cos}')
    angle = calc_angle_by_sincos(sin, cos)

    return angle


points = [(0, 0), (0, 0), (0, 0)]
current_p_index = 0
img = None


# mouse callback function
def draw_angle(event, x, y, flags, param):
    global points, current_p_index, img
    if event == cv.EVENT_LBUTTONDOWN:
        if current_p_index == 0:
            img = np.zeros((800, 800, 3), dtype=np.uint8)

        points[current_p_index] = (x, y)
        cv.circle(img, (x, y), 10, (0, 0, 255), -1, cv.LINE_AA)

        if current_p_index > 0:
            last_p = points[current_p_index - 1]
            cv.line(img, (x, y), last_p, (255, 0, 0), 2, cv.LINE_AA)

        current_p_index += 1
        if current_p_index == 3:
            angle = calc_angle(points[0], points[1], points[2])
            print(f'angle = {angle}')
            current_p_index = 0
            start_angle = calc_angle_by_axis_x(get_vector(points[1], points[0]))
            end_angle = calc_angle_by_axis_x(get_vector(points[1], points[2]))
            if end_angle < start_angle:
                end_angle += 360
            print(f'other_angle: {end_angle - start_angle}')
            # end_angle = start_angle + angle
            print(f'start_angle = {start_angle}, end_angle = {end_angle}')
            cv.ellipse(img, points[1], (15, 15), 0, start_angle, end_angle, (0, 255, 0), -1, cv.LINE_AA)

        cv.imshow('img', img)


if __name__ == '__main__':
    cv.namedWindow('img', cv.WINDOW_NORMAL)
    img = np.zeros((800, 800, 3), dtype=np.uint8)
    cv.imshow('img', img)
    cv.setMouseCallback('img', draw_angle)

    cv.waitKey()
    cv.destroyAllWindows()

二。稍加说明

1.demo使用方法

直接运行,在画布上依次按鼠标左键,点出点1,点2,点3,然后触发角度计算,绿色部分表示算的是哪个角。

注:按照上篇的说法,应该是点1绕点2逆时针旋转到点3。但是这里做了个变化,不是逆时针了,是顺时针了!为啥呢,因为在opencv中,y轴正方向是向下的,所以如果要逆时针旋转,那上篇的旋转公式得变一下才行。如果不想改变也很简单,就是定为由点1绕点2顺时针旋转到点3,即我们要求的大角。

图1

 运行结果如下图,红框里就是我们要求的值,绿框里的两个角度其实就图画的绿色的椭圆的起始角和终止角。 

 2.疑问

(1)这里会发现一个问题,其实不一定非得通过旋转公式来计算出旋转角,直接用终止边(图1的边2_3)的角度减去起始边(图1的边2_1)的角度,就可以得出旋转角的角度,但是这个角度有可能会小于0,此时直接把它加上360度,就OK了。其实这里可能为负的情况,就是起始边到终止边跨0度的问题,比如起始边是350度,终止边是10度,这样其实是顺时针转了20度,但是10-350会得到-340,再加360,就是20度啦。

3.更简单的方法

但是如果我们只想知道这个角是不是大于180度的话,其实还有一种结合旋转公式的更简单的判断方法,如下图,不再去求ABC的角,而是AB向量与BC向量的夹角(不过仍然是大角的概念),具体来说就是在AB延长线上取一点D,求的就是DBC大角,所以是D绕点B顺时针转到C的角度(注意这里顺时针是针对opencv y轴向下的情况)。

就下图来说求出来的DBC大角肯定是大于180度了,其sin值会小于0,相反,其对应的ABC就是小于180度。

再来看一个大角ABC大于180度的情况,此时DBC是小于180度的,则其sin值大于0

所以综上,由旋转公式求DBC的sin值,小于0,则ABC是小于180度,否则大于180度(如果要看0度,那就是等于0喽)

直接放上修改后的代码。

import cv2 as cv
import numpy as np
import math


def get_vector(p_from, p_to):
    return p_to[0] - p_from[0], p_to[1] - p_from[1]


def get_unit_vector(v):
    """
    获取单位向量
    """
    x, y = v
    length = (x ** 2 + y ** 2) ** 0.5
    return x / length, y / length


def calc_angle_by_sincos(sin, cos):
    if sin >= 0 and cos >= 0:
        angle = math.asin(sin)
    elif cos < 0:
        angle = math.pi - math.asin(sin)
    elif sin < 0 and cos >= 0:
        angle = math.asin(sin) + math.pi * 2
    else:
        raise ValueError(f'ignore case: sin: {sin}, cos: {cos}')
    angle = angle * 180 / math.pi
    return angle


def calc_angle_by_axis_x(v):
    """
    由向量计算与x轴的夹角,x轴顺时针到向量
    """
    x, y = get_unit_vector(v)
    return calc_angle_by_sincos(y, x)


def judge_angle(p_a, p_b, p_c):
    """
    角点为p_b,由b_a顺时针转到b_c的角
    注:y轴向下,就是顺时针,比如在opencv中。
       y轴向上,就是逆时针。所以顺逆只在一念之间~~
    """
    print(f'p_a: {p_a}, p_b: {p_b}, p_c: {p_c}')
    v_ab = get_vector(p_a, p_b)
    v_bc = get_vector(p_b, p_c)

    v_ab, v_bc = get_unit_vector(v_ab), get_unit_vector(v_bc)
    x1, y1 = v_ab
    x2, y2 = v_bc

    # 求解旋转方程
    a = np.array([[-y1, x1], [x1, y1]], dtype=np.float32)
    b = np.array([x2, y2], dtype=np.float32)
    result_flag, (sin, cos) = cv.solve(a, b)
    sin = sin[0]
    cos = cos[0]
    print(f'sin = {sin}, cos = {cos}')

    return sin < 0


points = [(0, 0), (0, 0), (0, 0)]
current_p_index = 0
img = None


# mouse callback function
def draw_angle(event, x, y, flags, param):
    global points, current_p_index, img
    if event == cv.EVENT_LBUTTONDOWN:
        if current_p_index == 0:
            img = np.zeros((800, 800, 3), dtype=np.uint8)

        points[current_p_index] = (x, y)
        cv.circle(img, (x, y), 10, (0, 0, 255), -1, cv.LINE_AA)

        if current_p_index > 0:
            last_p = points[current_p_index - 1]
            cv.line(img, (x, y), last_p, (255, 0, 0), 2, cv.LINE_AA)

        current_p_index += 1
        if current_p_index == 3:
            result = judge_angle(points[0], points[1], points[2])
            print('小于180' if result else '大于180')
            current_p_index = 0
            start_angle = calc_angle_by_axis_x(get_vector(points[1], points[0]))
            end_angle = calc_angle_by_axis_x(get_vector(points[1], points[2]))
            if end_angle < start_angle:
                end_angle += 360
            print(f'other_angle: {end_angle - start_angle}')
            # end_angle = start_angle + angle
            print(f'start_angle = {start_angle}, end_angle = {end_angle}')
            cv.ellipse(img, points[1], (15, 15), 0, start_angle, end_angle, (0, 255, 0), -1, cv.LINE_AA)

        cv.imshow('img', img)


if __name__ == '__main__':
    cv.namedWindow('img', cv.WINDOW_NORMAL)
    img = np.zeros((800, 800, 3), dtype=np.uint8)
    cv.imshow('img', img)
    cv.setMouseCallback('img', draw_angle)

    cv.waitKey()
    cv.destroyAllWindows()

这里为什么说更简单呢,因为不用再根据正弦余弦的4种情况来求角,也没用到反正弦反余弦,只要判断一下sin值的正负就行了,是不是更简单一点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1200329.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【第六章】软件设计师 之 数据结构与算法基础

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 1、数据结构与算法基础 2、数据 3、稀疏矩阵…

【第三章】软件设计师 之 数据库系统

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 1、数据库系统前言 2、三级模式 - 两级映射…

爬虫项目(12):正则、多线程抓取腾讯动漫,Flask展示数据

文章目录 书籍推荐正则抓取腾讯动漫数据Flask展示数据 书籍推荐 如果你对Python网络爬虫感兴趣&#xff0c;强烈推荐你阅读《Python网络爬虫入门到实战》。这本书详细介绍了Python网络爬虫的基础知识和高级技巧&#xff0c;是每位爬虫开发者的必读之作。详细介绍见&#x1f44…

Netty入门指南之NIO Selector写操作

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Netty应用专栏_Aomsir的博客-CSDN博客 文章目录 参考文献前言操作演…

transfomer模型——简介,代码实现,重要模块解读,源码,官方

一、什么是transfomer Transformer是一种基于注意力机制&#xff08;attention mechanism&#xff09;的神经网络架构&#xff0c;最初由Vaswani等人在论文《Attention Is All You Need》中提出。它在自然语言处理&#xff08;NLP&#xff09;领域取得了巨大成功&#xff0c;特…

自定义Graph Component:1.1-JiebaTokenizer具体实现

JiebaTokenizer类继承自Tokenizer类&#xff0c;而Tokenizer类又继承自GraphComponent类&#xff0c;GraphComponent类继承自ABC类&#xff08;抽象基类&#xff09;。本文使用《使用ResponseSelector实现校园招聘FAQ机器人》中的例子&#xff0c;主要详解介绍JiebaTokenizer类…

JavaScript_动态表格_添加功能

1、动态表格_添加功能.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>动态表格_添加功能</title><style>table{border: 1px solid;margin: auto;width: 100%;}td,th{text-align: ce…

Python:Unittest框架快速入门:用例、断言、夹具、套件、HTML报告、ddt数据驱动

快速看了套Unittest的入门教程 软件测试全套资料赠送_哔哩哔哩_bilibili软件测试全套资料赠送是快速入门unittest测试框架&#xff01;全实战详细教学&#xff0c;仅此一套&#xff01;的第1集视频&#xff0c;该合集共计11集&#xff0c;视频收藏或关注UP主&#xff0c;及时了…

ElasticSearch学习和使用 (使用head软件可视化es数据)

使用步骤 直接使用 Elasticsearch的安装和使用 下载Elasticsearch6.2.2的zip包&#xff0c;并解压到指定目录&#xff0c;下载地址&#xff1a;https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2运行bin目录下的elasticsearch.bat启动Elasticsearch安…

【Python】python读取,显示,保存图像的几种方法

一、PIL&#xff1a;Python Imaging Library&#xff08;pillow&#xff09; PIL读取图片不直接返回numpy对象&#xff0c;可以用numpy提供的函数np.array()进行转换&#xff0c;亦可用Image.fromarray()再从numpy对象转换为原来的Image对象&#xff0c;读取&#xff0c;显示&…

Deepsort项目详解

一、目标追踪整体代码 代码目录如下图所示&#xff1a; 、 追踪相关代码&#xff1a; 检测相关代码和权重 调用 检测 和 追踪的代码&#xff1a; 首先代码分为三个部分&#xff1a; 目标追踪的相关代码和权重目标检测相关代码和权重&#xff0c;这里用的是yolov5.5目标检…

c语言练习11周(6~10)

输入任意字串&#xff0c;将串中除了首尾字符的其他字符升序排列显示&#xff0c;串中字符个数最多20个。 题干 输入任意字串&#xff0c;将串中除了首尾字符的其他字符升序排列显示&#xff0c;串中字符个数最多20个。输入样例gfedcba输出样例gbcdefa 选择排序 #include<s…

java--JDBC学习

文章目录 今日内容0 复习昨日1 JDBC概述2 JDBC开发步骤2.1 创建java项目2.2 导入mysql驱动包2.2.1 复制粘贴版本2.2.2 idea导入类库版本 2.3 JDBC编程 3 完成增删改3.1 插入3.2 更新3.3 删除 4 查询结果集ResultSet【重要】5 登录案例【重要】6 作业 今日内容 0 复习昨日 1 JDB…

数据结构:树的存储结构(孩子兄弟表示法,树和森林的遍历)

目录 1.树的存储结构1.双亲表示法&#xff08;顺序存储&#xff09;1.优缺点 2.孩子表示法&#xff08;顺序链式存储&#xff09;3.孩子兄弟表示法&#xff08;链式存储&#xff09;4.森林与二叉树的转换 2.树的遍历1.先根遍历2.后根遍历3.层序遍历 3.森林的遍历1.先序遍历2.中…

汉明距离(Java)

两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。 给你两个整数 x 和 y&#xff0c;计算并返回它们之间的汉明距离。 方法1:使用内置函数 class Solution {public int hammingDistance(int x, int y) {return Integer.bitCount(x ^ y);} }方法2:移位实…

Flutter:改变手机状态栏颜色,与appBar状态颜色抱持一致

前言 最近在搞app的开发&#xff0c;本来没怎么注意appBar与手机状态栏颜色的问题。但是朋友一说才注意到这两种的颜色是不一样的。 我的app 京东 qq音乐 这样一对比发现是有的丑啊&#xff0c;那么如何实现呢&#xff1f; 实现 怎么说呢&#xff0c;真不会。百度到的一些是…

java的类和继承构造

一些小技巧 类和对象 什么是类&#xff0c;对象&#xff0c;方法&#xff1f; 在下面的 Java 代码中&#xff0c;定义了一个名为 Person 的类&#xff0c;并提供了构造方法来初始化对象的属性。类中定义了 eat、sleep 和 work 三个方法&#xff0c;用于表示人的行为。在 main 方…

ValueError: ‘x‘ and ‘y‘ must have the same size

ValueError: ‘x’ and ‘y’ must have the same size 问题描述 出错代码 axes[0].errorbar(dates_of_observation, observed_lai, yerrstd_lai, fmt"o")X是观测的日期&#xff0c;16天&#xff0c;而且数据也是对应的16个&#xff0c;为什么不对应呢&#xff1f;…

python工具CISCO ASA设备任意文件读取

​python漏洞利用 构造payload&#xff1a; /CSCOT/translation-table?typemst&textdomain/%2bCSCOE%2b/portal_inc.lua&default-language&lang../漏洞证明&#xff1a; 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&#xff0c;万分感谢。 免…

git的分支及标签使用及情景演示

目录 一. 环境讲述 二.分支 1.1 命令 1.2情景演练 三、标签 3.1 命令 3.2 情景演示 ​编辑 一. 环境讲述 当软件从开发到正式环境部署的过程中&#xff0c;不同环境的作用如下&#xff1a; 开发环境&#xff1a;用于开发人员进行软件开发、测试和调试。在这个环境中…