当前位置: 首页 > news >正文

如何判断一个角是否大于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,即我们要求的大角。

图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 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)

理论计算见上一篇&#xff1a; 如何判断一个角是否大于180度&#xff1f;_kv1830的博客-CSDN博客 此篇为代码实现 一。直接上代码&#xff1a; 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&#xff09;link的拓扑结构2&#xff09;link链接示例3&#xff09;link前继后继4&#xff09;道路link规则 3、road-type介…...

springboot的配置信息的设置和读取(application.properties/application.yml)

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

Deepsort项目详解

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

C语言证明一个偶数总能表示为两个素数之和。输入一个偶数并将其分解为两个素数

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

Python 的 datetime 模块

目录 简介 一、date类 &#xff08;一&#xff09;date 类属性 &#xff08;二&#xff09;date 类方法 &#xff08;三&#xff09;实例属性 &#xff08;四&#xff09;实例的方法 二、time类 &#xff08;一&#xff09;time 类属性 &#xff08;二&#xff09;tim…...

Termius for Mac:掌控您的云端世界,安全高效的SSH客户端

你是否曾经在Mac上苦苦寻找一个好用的SSH客户端&#xff0c;让你能够远程连接到Linux服务器&#xff0c;轻松管理你的云端世界&#xff1f;现在&#xff0c;我们向你介绍一款强大而高效的SSH客户端——Termius。 Termius是一款专为Mac用户设计的SSH客户端&#xff0c;它提供了…...

Ubuntu 下监控并自动重启网卡

很多时候网站服务器挂掉也可能是因为网卡挂掉了&#xff0c;如果你网站不能访问时 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 &#xff0c;和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。 题目数据保证答案符合 32 位整数范围。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3], target 4 输出&#xff1a;7 解释&#…...

【OpenCV】计算视频的光流并跟踪物体calcOpticalFlowPyrLK

一、介绍 计算光流可以使用OpenCV的calcOpticalFlowPyrLK方法&#xff0c;cv2.calcOpticalFlowPyrLK是OpenCV库中的一个函数&#xff0c;用于计算稀疏光流。它实现的是Lucas-Kanade方法&#xff0c;这是一种常用的光流计算方法。 光流是图像中物体运动的近似表示&#…...

C语言进阶

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

Linux之gdb

gdb就是一个Linux的调试工具&#xff0c;类似与vs里面的调试 可执行程序也有格式&#xff0c;不是简单的二进制堆砌...

100天精通风控建模(原理+Python实现)——第3天:风控建模中如何处理缺失值?

风控模型已在各大银行和公司都实际运用于业务,用于营销和风险控制等。    之前已经阐述了100天精通风控建模(原理+Python实现)——第1天:什么是风控建模?    100天精通风控建模(原理+Python实现)——第2天:风控建模有什么目的?    接下来看下100天精通风控建模(原理…...

Leetcode—680.验证回文串II【简单】

2023每日刷题&#xff08;二十七&#xff09; 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五种数据类型及命令操作(二)

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

低代码信创开发核心技术(三):MDA模型驱动架构及元数据系统设计

前言 写最后一篇文章的时候&#xff0c;我本人其实犹豫了半年&#xff0c;在想是否发布出这篇文章&#xff0c;因为可能会动了很多人的利益。所以这篇文章既是整个低代码信创开发的高度总结&#xff0c;也是最为精华的一部分&#xff0c;它点明了低代码中最为核心的技术。虽然…...

HslCommunication模拟西门子读写数据

导入HslCommunication C#端代码&#xff08;上位机&#xff09; 这里要注意的是上位机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模块 此模块是封装所有用例的基类 比如说&#xff1a;所有用例要用到的元素定位&#xff0c;以及输入框输入&#xff0c;点击&#xff0c;下拉等等公共方法import unittest #导入unittest 框架 from time import *# 调试代码…...

线性判别分析(Linear Discriminant Analysis,LDA)

Linear Discriminant Analysis&#xff08;LDA&#xff09; 输入&#xff1a; 原始数据$D((x_1,y_1),(x_2,y_2),...,(x_m,y_m)$ 、​ 类别标签$Y[y_1,y_2,...,y_n]$、​ 降维到的维度d输出&#xff1a; 投影矩阵W、投影后的样本$Z$、算法步骤&#xff1a; 1.计算类内散度…...

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

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

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...