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

理论计算见上一篇:

如何判断一个角是否大于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值的正负就行了,是不是更简单一点。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(头哥)多表查询与子查询

目录 第1关&#xff1a;查询每个学生的选修的课程信息 第2关&#xff1a;查询选修了“数据结构”课程的学生名单 第3关&#xff1a;查询“数据结构”课程的学生成绩单 第4关&#xff1a;查询每门课程的选课人数 第5关&#xff1a;查询没有选课的学生信息 第6关&#xff1a…

Doris:读取Doris数据的N种方法

目录 1.MySQL Client 2.JDBC 3. 查询计划 4.Spark Doris Connector 5.Flink Doris Connector 1.MySQL Client Doris 采用 MySQL 协议&#xff0c;高度兼容 MySQL 语法&#xff0c;支持标准 SQL&#xff0c;用户可以通过各类客户端工具来访问 Doris。登录到doris服务器后&a…

华为ensp:ospf动态路由

ip已配置好了 &#xff0c;现在进入路由器去宣告网段 R1 进入系统视图 ospf 1 area 1 network 192.168.1.0 0.0.0.255 network 1.1.1.0 0.0.0.255 R2 进入系统视图 ospf 1area 1 network 1.1.1.0 0.0.0.255 quit area 0 network 192.168.2.0 0.0.0.255 network 2.2…

上机4KNN实验4

目录 编程实现 kNN 算法。一、步骤二、实现代码三、总结知识1、切片2、iloc方法3、归一化4、MinMaxScale&#xff08;&#xff09;5、划分测试集、训练集6、KNN算法 .py 编程实现 kNN 算法。 1、读取excel表格存放的Iris数据集。该数据集有5列&#xff0c;其中前4列是条件属性…

[CISCN 2023 西南]do_you_like_read

打开题目&#xff0c;大概是一个购买书籍的网站&#xff0c;有登陆的功能 我们可以先分析下给的源码 在admin.php中会验证是否为admin用户 我们尝试爆破下密码&#xff0c;爆出来为admin123 登陆后发现存在文件上传漏洞 我们分析下源码 存在文件后缀检测&#xff0c;如果为p…

交换机工作原理

交换机工作原理 交换机功能&#xff1a;端口扩展&#xff08;默认同一网络&#xff09;&#xff0c;如果只是两台设备进行通信&#xff0c;可以直接连接这两台设备而不用交换机&#xff0c;但如果设备较多&#xff0c;设备没有那么多接口&#xff0c;那么这个时候就需要交换机…

三分钟学完Git版本控制常用指令

基本指令 git clone [url] 克隆远程仓库到本地 git clone https://gitee.com/mayun2023a/mprpc.git2.git checkout -b xxx 切换至新分支xxx&#xff08;相当于复制了remote的仓库到本地的xxx分支上) 3.修改或者添加本地代码&#xff08;部署在硬盘的源文件上&#xff09; 4.g…

Django配置文件,request,链接mysql方法,Orm简介

三板斧问题(views.py) HttpResponse # 返回的是字符串render # 渲染一个HTML静态文件&#xff0c;模板文件redirect # 重定向的 在视图文件中得视图函数必须要接收一个形参request&#xff0c;并且&#xff0c;视图函数也要有返回值&#xff…

着实不错的自适应大邻域搜索算法ALNS

文章目录 引言演进路线邻域搜索&#xff0c;NS变邻域搜素&#xff0c;VDNS大邻域搜索&#xff0c;LNS自适应大邻域搜索&#xff0c;ALNS 代码实现34个国内城市的TSP测试集XQF131 相关阅读 引言 之前介绍的差分进化算法和蚁群算法分别适用于求解连续优化问题和组合优化问题&…

【第五章】软件设计师 之 系统安全分析与设计

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 1、信息系统安全属性 2、对称加密与非对称加…

Redis五种数据类型及命令操作(二)

&#x1f388;个人公众号:&#x1f388; :✨✨✨ 可为编程✨ &#x1f35f;&#x1f35f; &#x1f511;个人信条:&#x1f511; 知足知不足 有为有不为 为与不为皆为可为&#x1f335; &#x1f349;本篇简介:&#x1f349; 本篇记录Redis五种数据类型及命令操作&#xff0c;如…

C语言--前置++与后置++

:自增1 注意区分前置和后置 前置&#xff1a;先&#xff0c;后使用 后置&#xff1a;先使用&#xff0c;后 --:自减1 注意区分前置和后置 前置&#xff1a;先-- &#xff0c;后使用 后置&#xff0c;先使用&#xff0c;后-- int main() {int i 10;//int j i;//前置,先…

网络编程学习笔记

参考&#xff1a; 套接字通信部分 《TCP/IP 网络编程》以及《TCP/IP网络编程》学习笔记 socket 编程 1. 字节序 字节序&#xff0c;顾名思义字节的顺序&#xff0c;就是大于一个字节类型的数据在内存中的存放顺序&#xff0c;也就是说对于单字符来说是没有字节序问题的&…

如何使用PHPStudy本地快速搭建网站并实现远程访问

文章目录 [toc]使用工具1. 本地搭建web网站1.1 下载phpstudy后解压并安装1.2 打开默认站点&#xff0c;测试1.3 下载静态演示站点1.4 打开站点根目录1.5 复制演示站点到站网根目录1.6 在浏览器中&#xff0c;查看演示效果。 2. 将本地web网站发布到公网2.1 安装cpolar内网穿透2…

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

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

选购护眼台灯,全网都没有说清一个关键点!——照度均匀度

网上关于护眼台灯的选购推荐帖子多如牛毛&#xff0c;好台灯选购要点大体可归纳为以下五点&#xff1a; RG0无蓝光危害&#xff08;豁免级蓝光危害&#xff0c;RG1为低蓝光危害、RG2、RG3分别为中度和高危危害&#xff09; 无眩光&#xff0c;无可视频闪&#xff08;不刺眼…

matlab 多自由度的车辆垂向振动模型 车辆平稳性研究

1、内容简介 略 17-可以交流、咨询、答疑 多自由度的车辆垂向振动模型 多自由度的车辆垂向振动模型&#xff0c;包含四分之一车体模型、半车模型和整车模型 垂向振动模型、四分之一车体模型、半车模型和整车模型 2、内容说明 略 3、仿真分析 略 4、参考论文 略 链接&…

内存映射:PS和PL DDR3的一些区别

之前写的一些资料&#xff1a; PS与PL互联与SCU以及PG082-CSDN博客 参考别人的资料&#xff1a; PL读写PS端DDR的设计_pl读写ps端ddr数据-CSDN博客 xilinx sdk、vitis查看地址_vitis如何查看microblazed地址_yang_wei_bk的博客-CSDN博客 可见&#xff0c;PS端的DDR3需要从…

git push origin masterEverything up-to-date

按住这个看一下很简单的问题&#xff0c;我在网上看了很多就是没找到能用的&#xff0c;最后找到了这个看起来写的很简单的一个文章&#xff0c;但他写的真的有用。 出现的问题 解决步骤

JavaScript逆向之Hook技术

Hook技术&#xff1a; 背景&#xff1a; ​ 在js逆向的过程种&#xff0c;当我们遇到加密参数&#xff0c;可以使用关键字全局搜素&#xff0c;跟栈&#xff0c;还有一种就是hook技术。跟栈就是比较麻烦&#xff0c;需要我们一个个找&#xff0c;hook技术就比较厉害了&#x…