如何判断一个角是否大于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 情景演示 编辑 一. 环境讲述 当软件从开发到正式环境部署的过程中,不同环境的作用如下: 开发环境:用于开发人员进行软件开发、测试和调试。在这个环境中…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
Java后端检查空条件查询
通过抛出运行异常:throw new RuntimeException("请输入查询条件!");BranchWarehouseServiceImpl.java // 查询试剂交易(入库/出库)记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…...
node.js的初步学习
那什么是node.js呢? 和JavaScript又是什么关系呢? node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说, 需要在node.js的环境上进行当JavaScript作为前端开发语言来说,需要在浏览器的环境上进行 Node.js 可…...
算法—栈系列
一:删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...
