如何判断一个角是否大于180度(2)
理论计算见上一篇:
如何判断一个角是否大于180度?_kv1830的博客-CSDN博客
此篇为代码实现
一。直接上代码:
import cv2 as cv
import numpy as np
import mathdef 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 = vlength = (x ** 2 + y ** 2) ** 0.5return x / length, y / lengthdef 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 * 2else:raise ValueError(f'ignore case: sin: {sin}, cos: {cos}')angle = angle * 180 / math.pireturn angledef 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_bax2, 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 anglepoints = [(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, imgif 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 += 1if current_p_index == 3:angle = calc_angle(points[0], points[1], points[2])print(f'angle = {angle}')current_p_index = 0start_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 += 360print(f'other_angle: {end_angle - start_angle}')# end_angle = start_angle + angleprint(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,即我们要求的大角。

运行结果如下图,红框里就是我们要求的值,绿框里的两个角度其实就图画的绿色的椭圆的起始角和终止角。
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 mathdef 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 = vlength = (x ** 2 + y ** 2) ** 0.5return x / length, y / lengthdef 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 * 2else:raise ValueError(f'ignore case: sin: {sin}, cos: {cos}')angle = angle * 180 / math.pireturn angledef 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_abx2, 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 < 0points = [(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, imgif 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 += 1if current_p_index == 3:result = judge_angle(points[0], points[1], points[2])print('小于180' if result else '大于180')current_p_index = 0start_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 += 360print(f'other_angle: {end_angle - start_angle}')# end_angle = start_angle + angleprint(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值的正负就行了,是不是更简单一点。
相关文章:

如何判断一个角是否大于180度(2)
理论计算见上一篇: 如何判断一个角是否大于180度?_kv1830的博客-CSDN博客 此篇为代码实现 一。直接上代码: import cv2 as cv import numpy as np import mathdef get_vector(p_from, p_to):return p_to[0] - p_from[0], p_to[1] - p_from…...

ASAM OpenDRIVE V1.7协议超详解(一)
文章目录 前言一、仿真场景的构成二、openDRIVE框架三、g_additionalData四、openDRIVE-header五、openDRIVE-road1、Road总拓扑结构2、Road-link介绍1)link的拓扑结构2)link链接示例3)link前继后继4)道路link规则 3、road-type介…...

springboot的配置信息的设置和读取(application.properties/application.yml)
springboot提供了两种配置信息的文件格式,application.properties和application.yml,基于直接明了,使用方便和高效的前提下下面的配置均采用yml格式配置, 注意 yml采用缩减方式来排列键后面紧跟冒号,然后空格&#x…...

Deepsort项目详解
一、目标追踪整体代码 代码目录如下图所示: 、 追踪相关代码: 检测相关代码和权重 调用 检测 和 追踪的代码: 首先代码分为三个部分: 目标追踪的相关代码和权重目标检测相关代码和权重,这里用的是yolov5.5目标检…...

C语言证明一个偶数总能表示为两个素数之和。输入一个偶数并将其分解为两个素数
完整代码: // 一个偶数总能表示为两个素数之和。输入一个偶数并将其分解为两个素数#include<stdio.h>//判断一个数n是否为素数 int isPrimeNumber(int n){//1不是素数if (n1){return 0;}for (int i 2; i <(n/2); i){//当有n能被整除时,不是素…...

Python 的 datetime 模块
目录 简介 一、date类 (一)date 类属性 (二)date 类方法 (三)实例属性 (四)实例的方法 二、time类 (一)time 类属性 (二)tim…...

Termius for Mac:掌控您的云端世界,安全高效的SSH客户端
你是否曾经在Mac上苦苦寻找一个好用的SSH客户端,让你能够远程连接到Linux服务器,轻松管理你的云端世界?现在,我们向你介绍一款强大而高效的SSH客户端——Termius。 Termius是一款专为Mac用户设计的SSH客户端,它提供了…...
Ubuntu 下监控并自动重启网卡
很多时候网站服务器挂掉也可能是因为网卡挂掉了,如果你网站不能访问时 SSH 也无效了一般都是这个问题。这时可以通过一个定时脚本监控网络并进行自动重启。 1 创建脚本 auto_restart_network.sh 4 5 6 7 8 9 #!/bin/bash ping www.baidu.com -c 1 >/dev/null i…...
377. 组合总和 Ⅳ
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。 题目数据保证答案符合 32 位整数范围。 示例 1: 输入:nums [1,2,3], target 4 输出:7 解释&#…...
【OpenCV】计算视频的光流并跟踪物体calcOpticalFlowPyrLK
一、介绍 计算光流可以使用OpenCV的calcOpticalFlowPyrLK方法,cv2.calcOpticalFlowPyrLK是OpenCV库中的一个函数,用于计算稀疏光流。它实现的是Lucas-Kanade方法,这是一种常用的光流计算方法。 光流是图像中物体运动的近似表示&#…...

C语言进阶
数组 在基础篇说过,数组实际上是构造类型之一,是连续存放的。 一维数组 定义 定义格式:[存储类型] 数据类型 数组名标识符[下标]; 下面分模块来介绍一下数组的定义部分的内容。 1、初始化和元素引用: 可以看到数组是连续存储…...

Linux之gdb
gdb就是一个Linux的调试工具,类似与vs里面的调试 可执行程序也有格式,不是简单的二进制堆砌...
100天精通风控建模(原理+Python实现)——第3天:风控建模中如何处理缺失值?
风控模型已在各大银行和公司都实际运用于业务,用于营销和风险控制等。 之前已经阐述了100天精通风控建模(原理+Python实现)——第1天:什么是风控建模? 100天精通风控建模(原理+Python实现)——第2天:风控建模有什么目的? 接下来看下100天精通风控建模(原理…...

Leetcode—680.验证回文串II【简单】
2023每日刷题(二十七) Leetcode—680.验证回文串II 实现代码 class Solution { public:bool judgeFunc(string s, int left, int right) {while(left < right) {if(s[left] ! s[right]) {return false;}left;right--;}return true;}bool validPalin…...

Redis五种数据类型及命令操作(二)
🎈个人公众号:🎈 :✨✨✨ 可为编程✨ 🍟🍟 🔑个人信条:🔑 知足知不足 有为有不为 为与不为皆为可为🌵 🍉本篇简介:🍉 本篇记录Redis五种数据类型及命令操作,如…...
低代码信创开发核心技术(三):MDA模型驱动架构及元数据系统设计
前言 写最后一篇文章的时候,我本人其实犹豫了半年,在想是否发布出这篇文章,因为可能会动了很多人的利益。所以这篇文章既是整个低代码信创开发的高度总结,也是最为精华的一部分,它点明了低代码中最为核心的技术。虽然…...

HslCommunication模拟西门子读写数据
导入HslCommunication C#端代码(上位机) 这里要注意的是上位机IP用的当前电脑的IP。 using HslCommunication; using HslCommunication.Profinet.Siemens; using System; using System.Collections.Generic; using System.ComponentModel; using Syste…...
多测师肖sir_高级金牌讲师_ui自动化po框架版本02
ui自动化po框架版本02 一、 pages下的BasePage.py模块 此模块是封装所有用例的基类 比如说:所有用例要用到的元素定位,以及输入框输入,点击,下拉等等公共方法import unittest #导入unittest 框架 from time import *# 调试代码…...
线性判别分析(Linear Discriminant Analysis,LDA)
Linear Discriminant Analysis(LDA) 输入: 原始数据$D((x_1,y_1),(x_2,y_2),...,(x_m,y_m)$ 、 类别标签$Y[y_1,y_2,...,y_n]$、 降维到的维度d输出: 投影矩阵W、投影后的样本$Z$、算法步骤: 1.计算类内散度…...

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

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...

GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...