目录
一 前言
在本次项目中,我们将会使用鼠标构建一个角,并显示这个角的度数;即通过点击鼠标来生成两条线,如果这两条线相交就会形成一个夹角,我们要做的就是测量这个夹角的度数。和之前的项目一样,本项目也是博主在Github中找到的比较有趣的项目,大家如果感兴趣可以尝试着复现一遍,如果对其他项目感兴趣可以关注下面这专栏,有时间我会更新一些比较有趣的项目分享给大家:
GitHub 计算机视觉项目实践
Now, let's start!
二 实现步骤
先来简单介绍一下本次的任务,我们要通过点击鼠标生成三个点,而这三个点刚好能够组成两条相交的射线,接下我们使用一些简单的数学知识就可以得出这个角的度数了。不难看出,本次的项目并没有太大的难度,主要是使用数学方法计算角度。
1 需要的第三方库
在本次项目中首先是要对图像中的角度进行测量,所以需要的第一个库是 OpenCV;然后需要计算角度,免不了数学计算,因此还需要 math 库,使用这两个库就可以实现本次项目了。
2 读取图像
本项目是在图像中测量角度,所以第一步需要做的是读取目标图像。本次实验中我使用的是随便找的一幅图像,只要是使用 CV2 第三方库能够读取就到图像就可以。
读取图像的代码比较简单,如下:
import cv2
import math
path = 'test.jpg' #图像读取路径
img = cv2.imread(path) #读取图像
如果你想将图像显示出来也可以:
3 获取鼠标点
读取图像之后我们要操作的是能够让鼠标的点击在图像中起作用(点击左键就可以创建一个点,通过三次点击不同的点得到一个角)。在这里我们创建一个 mousepoint() 函数来完成这个任务。
(1)打印鼠标点击的位置(演示,不一定要实现)
通过下面的函数就可以实现鼠标点的检测并将点击的坐标,本过程只是显示鼠标的点击位置,在项目中不一定实现
def mousePoints(event, x, y, flags, params):
if event == cv2.EVENT_LBUTTONDOWN: #检测是否有鼠标左键按下
print(x, y)
cv2.setMouseCallback('Image', mousePoints) #鼠标的点击返回到图像中
通过点击就可以打印出鼠标的位置
(2)创建列表存储坐标并将点击生成的点绘制出来
每测量一个角度我们要生成不止一个点,所以我们可以将这些点的坐标存储到一个列表中。
pointsList = [] #创建列表来存储鼠标点击的坐标点(这行代码并不在定义的函数中)
pointsList.append([x, y])#将坐标点存储在列表中(这行代码应该在函数中)
cv2.circle(img, (x, y), 5, (0, 0, 255), cv2.FILLED) #在鼠标的点击处绘制一个实心圆
#上面的代码都是实现功能的方式,并不会全都排列在一起
将存有坐标点的列表打印下来如下:
4 计算三个点组成的角的度数
当我们的列表中存储的点的个数达到3的倍数(即图像中的出现完整的角度时,我们该计算这些角的度数并将其绘制在图中)
计算一个角的度数的方式如下(通过三角函数诱导公式就能得到):
创建一个函数来计算角的度数:
#------------------------------
#定义函数计算两点之间的斜率
#------------------------------
def gradient(pt1, pt2):
return (pt2[1] - pt1[1]) / (pt2[0] - pt1[0])
#----------------------------
#定义函数计算角的度数
#---------------------------
def getAngle(pointsList):
pt1, pt2, pt3 = pointsList[-3:] #列表中最后的三个坐标点
m1 = gradient(pt1, pt2) #计算第一个点和第二个点之间的斜率
m2 = gradient(pt1, pt3) #计算第一个点和第三个点之间的斜率
angR = math.atan((m2 - m1) / (1 + (m2 * m1))) #计算正切值
angD = round(math.degrees(angR)) #将弧度转化为角度
cv2.putText(img, str(angD), (pt1[0] - 40, pt1[1] - 20), cv2.FONT_HERSHEY_COMPLEX,
1.5, (0, 0, 255), 2) #绘制文本
5 润色(绘制线段构成角度)
其实到第四步我们的目标已经达成了,将前面的代码整合起来就可以实现测量角度了,但我们可以在添加一些来绘制线段构成角度,代码如下:
# -------------------
#绘制线段构成一个角度
# -------------------
size = len(pointsList)
if size != 0 and size % 3 != 0:
cv2.line(img, tuple(pointsList[round((size - 1) / 3) * 3]), (x, y), (0, 0, 255), 2)
通过以上的步骤就能实现本次的角度测量了
由于线段宽度以及一些因素的影响,误差在所难免,感兴趣可以简单尝试一下。
三 整体代码
整体代码如下:
"""
Author:XiaoMa
date:2022/03/30
"""
import cv2
import math
path = 'test.jpg' #图像读取路径
img = cv2.imread(path) #读取图像
pointsList = [] #创建列表来存储鼠标点击的坐标点
#-------------------------------
#定义函数来检测鼠标动作
#-------------------------------
def mousePoints(event, x, y, flags, params):
if event == cv2.EVENT_LBUTTONDOWN: #检测是否有鼠标左键按下
# -------------------
#绘制线段构成一个角度
# -------------------
size = len(pointsList)
if size != 0 and size % 3 != 0:
cv2.line(img, tuple(pointsList[round((size - 1) / 3) * 3]), (x, y), (0, 0, 255), 2) #在两点之间绘制直线
cv2.circle(img, (x, y), 5, (0, 0, 255), cv2.FILLED) #在鼠标的点击处绘制一个实心圆
pointsList.append([x, y])#将坐标点存储在列表中
print(pointsList)
#------------------------------
#定义函数计算两点之间的斜率
#------------------------------
def gradient(pt1, pt2):
return (pt2[1] - pt1[1]) / (pt2[0] - pt1[0])
#----------------------------
#定义函数计算角的度数
#---------------------------
def getAngle(pointsList):
pt1, pt2, pt3 = pointsList[-3:] #列表中最后的三个坐标点
m1 = gradient(pt1, pt2) #计算第一个点和第二个点之间的斜率
m2 = gradient(pt1, pt3) #计算第一个点和第三个点之间的斜率
angR = math.atan((m2 - m1) / (1 + (m2 * m1))) #计算正切值
angD = round(math.degrees(angR)) #将弧度转化为角度
cv2.putText(img, str(angD), (pt1[0] - 40, pt1[1] - 20), cv2.FONT_HERSHEY_COMPLEX,
1.5, (0, 0, 255), 2) #绘制文本
while True:
if len(pointsList) % 3 == 0 and len(pointsList) != 0: #列表长度为3时得到角度
getAngle(pointsList)
cv2.imshow('Image', img)
cv2.setMouseCallback('Image', mousePoints) #鼠标的点击返回到图像中
#-------------------------------
#按下q键清除图像中的点(刷新图像)
#-------------------------------
if cv2.waitKey(1) & 0xFF == ord('q'):
pointsList = []
img = cv2.imread(path)
四 结束语
本次项目的实现较为简单,大家都可以尝试一下,原理也是使用高中的知识就能搞懂,简单的不达鸟,简单的一塌糊涂啊!!