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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

xlua游戏热更新(lua访问C#)

CS.UnityEngine静态方法访问unity虚拟机 创建游戏物体 CS.UnityEngine.GameObject(new by lua);静态属性 CS.UnityEngine.GameObject(new by lua); -- 创建 local camera CS.UnityEngine.GameObject.Find(Main Camera); --查找 camera.name Renamed by Lua;访问组件 loca…

区块链多链数字钱包开发

随着区块链技术的不断发展&#xff0c;多链数字钱包的开发逐渐成为热门领域。多链数字钱包是一种可以支持多种区块链网络的数字钱包&#xff0c;用户可以使用它来存储、管理和转移不同的数字资产。本文将探讨多链数字钱包的开发背景、市场需求、技术实现和未来趋势等方面。 一、…

02:2440---时钟体系

目录 一:时钟控制 1:基本概念 2:时钟结构图 3:结构图分析 4:总线 5:寄存器 A:FCLK--MPLLCON B:HCLK和PCLK--CLKDIVN C:注意 二:上电复位 1:上电复位 2:时钟选择 三:代码 一:时钟控制 1:基本概念 S3C2440A中的时钟控制逻辑可以产生所需的时钟信号&#xff0c;包括C…

手机地磁传感器与常见问题

在手机中&#xff0c;存在不少传感器&#xff0c;例如光距感&#xff0c;陀螺仪&#xff0c;重力加速度&#xff0c;地磁等。关于各传感器&#xff0c;虽功能作用大家都有所了解&#xff0c;但是在研发设计debug过程中&#xff0c;却总是会遇到很多头疼的问题。关于传感器&…

数据结构:树的存储结构(孩子兄弟表示法,树和森林的遍历)

目录 1.树的存储结构1.双亲表示法&#xff08;顺序存储&#xff09;1.优缺点 2.孩子表示法&#xff08;顺序链式存储&#xff09;3.孩子兄弟表示法&#xff08;链式存储&#xff09;4.森林与二叉树的转换 2.树的遍历1.先根遍历2.后根遍历3.层序遍历 3.森林的遍历1.先序遍历2.中…

fastANI-基因组平均核酸一致性(ANI)计算

文章目录 简介安装使用Many to Man-使用基因组路径作为输入One to One 结果其他参数说明可视化两个基因组之间的保守区域并行化 简介 FastANI 是为快速计算全基因组平均核苷酸同一性&#xff08;Average Nucleotide Identity&#xff0c;ANI&#xff09;而开发的&#xff0c;无…

人工智能与教育:未来的技术融合

人工智能与教育&#xff1a;未来的技术融合 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;逐渐渗透到我们生活的方方面面&#xff0c;包括教育领域。AI与教育的结合&#xff0c;有望引发一场教育变革&#xff0c;提高教学效果&#xff0c;实现个性化学习&…

【自定义类型:结构体】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1. 结构体类型的声明 1.1 结构体的概念 1.2 结构的声明 ​编辑 1.3 特殊的声明 1.4 结构的自引用 2. 结构体变量的创建和初始化 3. 结构成员访问操作符 4. 结构体内…

matlab GUI界面实现ZieglerNicholas调节PID参数

1、内容简介 略 11-可以交流、咨询、答疑 ZieglerNicholas、PID、GUI 2、内容说明 GUI界面实现ZieglerNicholas调节PID参数 通过ZieglerNicholas调节PID参数&#xff0c;设计了GUI 3、仿真分析 略 4、参考论文 略 链接&#xff1a;https://pan.baidu.com/s/1yQ1yDfk-_…

Revit 平面的圆弧,空间的椭圆弧

大家对Revit的空间曲线那么理解,如何用代码创建空间的椭圆弧,,上看是圆弧,正面看是椭圆? 直接放代码: Document doc = commandData.Application.ActiveUIDocument.Document; Autodesk.Revit.DB.XYZ center = new Autodesk.Revit.DB.XYZ(0, 0, 0); …

杂记杂记杂记

目录 Mybatis分页插件原理&#xff1f; ThreadLocal? 树形表的标记字段是什么&#xff1f;如何查询MySQL树形表&#xff1f; Mybatis的ResultType和ResultMap的区别&#xff1f; #{}和${}有什么区别&#xff1f; 系统如何处理异常&#xff1f; Mybatis分页插件原理&#…

基于SSM框架的高校试题管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

Flutter:改变手机状态栏颜色,与appBar状态颜色抱持一致

前言 最近在搞app的开发&#xff0c;本来没怎么注意appBar与手机状态栏颜色的问题。但是朋友一说才注意到这两种的颜色是不一样的。 我的app 京东 qq音乐 这样一对比发现是有的丑啊&#xff0c;那么如何实现呢&#xff1f; 实现 怎么说呢&#xff0c;真不会。百度到的一些是…

c语言练习11周(6~10)

输入任意字串&#xff0c;将串中除了首尾字符的其他字符升序排列显示&#xff0c;串中字符个数最多20个。 题干 输入任意字串&#xff0c;将串中除了首尾字符的其他字符升序排列显示&#xff0c;串中字符个数最多20个。输入样例gfedcba输出样例gbcdefa 选择排序 #include<s…

java的类和继承构造

一些小技巧 类和对象 什么是类&#xff0c;对象&#xff0c;方法&#xff1f; 在下面的 Java 代码中&#xff0c;定义了一个名为 Person 的类&#xff0c;并提供了构造方法来初始化对象的属性。类中定义了 eat、sleep 和 work 三个方法&#xff0c;用于表示人的行为。在 main 方…

k8s上对Pod的管理部分详解

目录 一.Pod结构介绍 1.user container 2.pause 二.Pod工作形式介绍 1.自主式pod 2.控制器管理下的pod 三.对Pod的操作介绍 1.run/apply -f/create -f运行pod 2.get查看pod信息 3.exec操作运行中的pod &#xff08;1&#xff09;进入运行中的pod &#xff08;2&…

React的refs和表单组件总结

React的refs和表单组件 react中refs的使用字符串形式的ref react核心就在于虚拟DOM&#xff0c;也就是React中不总是直接操页面的真实DOM元素&#xff0c;并且结合Diffing算法&#xff0c;可以做到最小化页面重绘&#xff0c;但有些时候不可避免我们需要一种方法可以操作我们定…

深入OpenCV Android应用开发

前言 OpenCV是Open Source Computer Vision library(开源的计算机视觉库)的缩写。它是使用最广泛的计算机视觉库。Opencv是计算机视觉领域常用的操作函数的集合&#xff0c;其自身由C/C编写而成&#xff0c;同时也提供了对Python、Java以及任意JVM语言的封装。考虑到大部分And…

【Springboot】基于注解式开发Springboot-Vue3整合Mybatis-plus实现分页查询(二)——前端el-pagination实现

系列文章 【Springboot】基于注解式开发Springboot-Vue3整合Mybatis-plus实现分页查询—后端实现 文章目录 系列文章系统版本实现功能实现思路后端传入的数据格式前端el-table封装axois接口引入Element-plus的el-pagination分页组件Axois 获取后台数据 系统版本 后端&#xf…

内存映射: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需要从…