当前位置: 首页 > 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;用于开发人员进行软件开发、测试和调试。在这个环境中…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

Java后端检查空条件查询

通过抛出运行异常&#xff1a;throw new RuntimeException("请输入查询条件&#xff01;");BranchWarehouseServiceImpl.java // 查询试剂交易&#xff08;入库/出库&#xff09;记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…...

node.js的初步学习

那什么是node.js呢&#xff1f; 和JavaScript又是什么关系呢&#xff1f; node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说&#xff0c; 需要在node.js的环境上进行当JavaScript作为前端开发语言来说&#xff0c;需要在浏览器的环境上进行 Node.js 可…...

算法—栈系列

一&#xff1a;删除字符串中的所有相邻重复项 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…...