OpenCV零基础实战项目2:测角

2023-10-13 09:20

本文主要是介绍OpenCV零基础实战项目2:测角,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简述

主要任务:获取鼠标点击点的位置信息(第一个点是顶点),绘制两条线构成的夹角,计算夹角并显示出来。

其他功能:读取图片,展示图片,键入q清空坐标点,键入d销毁所有窗口。

目录

  • 简述
  • 资源
  • 实现
    • 1. 获取点位置信息并显示
    • 2. 计算夹角
    • 3. 键入q清空坐标点,键入d销毁所有窗口
  • 实际操作中的问题和注意事项
  • 完整代码与结果示意

资源

视频资源:
(强推)OpenCV实战项目

图片:角度识别

实现

1. 获取点位置信息并显示

读取并显示图片:imread(), imshow().
鼠标响应:setMousecallback()。

void setMousecallback(const string& winname, MouseCallback onMouse, void* userdata=0)winname:窗口的名字onMouse:鼠标响应函数,回调函数。指定窗口里每次鼠标时间发生的时候,被调用的函数指针。 这个函数的原型应该为void on_Mouse(int event, int x, int y, int flags, void* param);userdate:传给回调函数的参数 

它的参数函数有固定参数设置,可以自定义名称,这里定义为mousePoints()

void on_Mouse(int event, int x, int y, int flags, void* param);
event是 CV_EVENT_*变量之一
x和y是鼠标指针在图像坐标系的坐标(不是窗口坐标系) 
flags是CV_EVENT_FLAG的组合, param是用户定义的传递到setMouseCallback函数调用的参数。

我们通过自定义的函数mousePoints()绘制出点击的点(cv.circle()),并绘制出两条线。该函数具体如下:

def mousePoints(event, x, y, flags, params):if event == cv.EVENT_LBUTTONDOWN:cv.circle(img, (x,y), 5, (0,0,255), cv.FILLED)pointList.append([x,y])#print(pointList)#draw out the linessize = len(pointList)if size != 1 and (size-1) % 3 != 0:cv.line(img, tuple(pointList[round((size-2)/3)*3]), (x,y), (0,0,255), 2)

当按下左键时,获取x和y坐标绘制,并通过append()加入pointList。

以三点为一组,第一个点(顶点)作为point1,第二或第三点作为point2. 此处除以三取整(round()四舍五入)再乘三就可以取到这一组的第一个点。
例如该组三点序号为6,7,8. pointList加入点[8]时,size=9,(9-2)/3取整乘3为6. pointList加入点[9]时,size=10,(10-2)/3取整乘3为9,恰是下一组的第一个点。事实上,我们需要的顶点需要应该为0,3,6,9等3的整倍数序号点。

2. 计算夹角

公式:
夹角计算公式

当获取一组点(三个)以后,可以调用自定义的函数getAngle()计算夹角度数。获取最后三个点,调用自定义的gradient()函数计算两条线的斜率,根据公式得到角度并打印。具体代码如下:

def gradient(pt1, pt2):#(y2-y1)/(x2-x1)return (pt2[1]-pt1[1])/(pt2[0]-pt1[0])def getAngle(pointList):pt1, pt2, pt3 = pointList[-3:]#the last 3 pointsm1 = gradient(pt1, pt2)m2 = gradient(pt1, pt3)angR = math.atan((m1-m2)/(1+m1*m2))angD = round(math.degrees(angR))cv.putText(img, str(angD), (pt1[0]-40, pt1[1]-20), cv.FONT_HERSHEY_COMPLEX, 1.5, (0,0,255), 2)

Math.atan()是arctan的计算,由正切值得到角度,再通过math.degrees()将弧度转化为角度。

3. 键入q清空坐标点,键入d销毁所有窗口

cv.waitkey(delay)在键入字符时返回一个对应的ASCII值,当它等于’q’时,清空pointList。当它等于’d’时,关闭窗口退出。由于我们在一个循环中,需要break才可以。代码如下:

    if cv.waitKey(1) & 0xFF == ord('d'):cv.destroyAllWindows()breakelif cv.waitKey(1) & 0xFF == ord('q'):pointList = []

其中,cv.waitKey(1) & 0xFF是因为不同系统返回的值不全是八位,与操作进行一个第八位的获取。

cv2.waitKey(1) returns the character code of the currently pressed key
and -1 if no key is pressed. the & 0xFF is a binary AND operation to
ensure only the single byte (ASCII) representation of the key remains
as for some operating systems cv2.waitKey(1) will return a code that
is not a single byte. ord(‘q’) always returns the ASCII representation
of ‘q’ which is 113 (0x71 in hex).

实际操作中的问题和注意事项

  1. Fail to close the window properly
    在这里插入图片描述
    While Ture: 环境内没有break

  2. Too much lines
    在这里插入图片描述
    cv.line(img, tuple(pointList[round((size-1)/3)*3]), (x,y), (0,0,255), 2)
    应该在if event == cv.EVENT_LBUTTONDOWN:下

完整代码与结果示意

import cv2 as cv
import mathpath = 'E:\\CV\\pics\\angle.png'
img = cv.imread(path)
pointList = []def mousePoints(event, x, y, flags, params):if event == cv.EVENT_LBUTTONDOWN:cv.circle(img, (x,y), 5, (0,0,255), cv.FILLED)pointList.append([x,y])#print(pointList)#draw out the linessize = len(pointList)if size != 1 and (size-1) % 3 != 0:cv.line(img, tuple(pointList[round((size-2)/3)*3]), (x,y), (0,0,255), 2)def gradient(pt1, pt2):#(y2-y1)/(x2-x1)return (pt2[1]-pt1[1])/(pt2[0]-pt1[0])def getAngle(pointList):pt1, pt2, pt3 = pointList[-3:]#the last 3 pointsm1 = gradient(pt1, pt2)m2 = gradient(pt1, pt3)angR = math.atan((m1-m2)/(1+m1*m2))angD = round(math.degrees(angR))cv.putText(img, str(angD), (pt1[0]-40, pt1[1]-20), cv.FONT_HERSHEY_COMPLEX, 1.5, (0,0,255), 2)while True:        if len(pointList) % 3 == 0 and len(pointList) != 0:getAngle(pointList)cv.imshow('image',img)cv.setMouseCallback('image', mousePoints)#delete the wrong pointsif cv.waitKey(1) & 0xFF == ord('d'):cv.destroyAllWindows()breakelif cv.waitKey(1) & 0xFF == ord('q'):pointList = []

结果:结果

这篇关于OpenCV零基础实战项目2:测角的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/202241

相关文章

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

配置springboot项目动静分离打包分离lib方式

《配置springboot项目动静分离打包分离lib方式》本文介绍了如何将SpringBoot工程中的静态资源和配置文件分离出来,以减少jar包大小,方便修改配置文件,通过在jar包同级目录创建co... 目录前言1、分离配置文件原理2、pom文件配置3、使用package命令打包4、总结前言默认情况下,

在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程

《在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程》本文介绍了在Java中使用ModelMapper库简化Shapefile属性转JavaBean的过程,对比... 目录前言一、原始的处理办法1、使用Set方法来转换2、使用构造方法转换二、基于ModelMapper

Java实战之自助进行多张图片合成拼接

《Java实战之自助进行多张图片合成拼接》在当今数字化时代,图像处理技术在各个领域都发挥着至关重要的作用,本文为大家详细介绍了如何使用Java实现多张图片合成拼接,需要的可以了解下... 目录前言一、图片合成需求描述二、图片合成设计与实现1、编程语言2、基础数据准备3、图片合成流程4、图片合成实现三、总结前

python实现简易SSL的项目实践

《python实现简易SSL的项目实践》本文主要介绍了python实现简易SSL的项目实践,包括CA.py、server.py和client.py三个模块,文中通过示例代码介绍的非常详细,对大家的学习... 目录运行环境运行前准备程序实现与流程说明运行截图代码CA.pyclient.pyserver.py参

nginx-rtmp-module构建流媒体直播服务器实战指南

《nginx-rtmp-module构建流媒体直播服务器实战指南》本文主要介绍了nginx-rtmp-module构建流媒体直播服务器实战指南,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. RTMP协议介绍与应用RTMP协议的原理RTMP协议的应用RTMP与现代流媒体技术的关系2

IDEA运行spring项目时,控制台未出现的解决方案

《IDEA运行spring项目时,控制台未出现的解决方案》文章总结了在使用IDEA运行代码时,控制台未出现的问题和解决方案,问题可能是由于点击图标或重启IDEA后控制台仍未显示,解决方案提供了解决方法... 目录问题分析解决方案总结问题js使用IDEA,点击运行按钮,运行结束,但控制台未出现http://