先放结论:
目录
相关库:
导入图片:
定义鼠标事件回调函数:
计算角度:
结果分析,发现原因:
完整代码:
接下来,就愉快地开始讲解这个项目的代码。
opencv库需要下载安装,math不用下载安装。
import cv2
import math
非常入门的导入图片。
path = 'test.png'
img = cv2.imread(path)
cv2.imshow('img', img)
cv2.waitKey(0)
event == cv2.EVENT_LBUTTONDOWN是用来判断鼠标是否按下:如果按下了,那么就在那个点画一个半径为5的、轮廓颜色为红色、填充了的圆形。
将鼠标按下的位置(x,y)存入pointsList数组里。
由于角度测量,需要确定三个点。因此使用一个循环不停的回调来获取鼠标点击的位置坐标。
pointsList = []
# 定义鼠标事件的回调函数
def mousePoints(event,x,y,flags,params):
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img,(x,y),5,(0,0,255),cv2.FILLED)
pointsList.append([x,y])
while True:
cv2.imshow('img', img)
# 调用回调函数:
cv2.setMouseCallback('img', mousePoints)
# 如果按下键盘上的q键,就会清空img窗口上的涂鸦,同时清空pointsList数组
if cv2.waitKey(1) & 0xFF == ord('q'):
pointsList = []
img = cv2.imread(path)
# 如果按下键盘上的esc键,那就关闭退出循环,关闭窗口
if cv2.waitKey(1) & 0xFF == 27:
break
结果:
但是咱们需要三个点,而且将这三个点画成两条线的样子组成一个角;
或者再画出三个点,这三个点连成新的两条线组成新角。以此类推。
这将如何操作?
咱们在鼠标回调函数里面加入一些东西,改成了以下的形式:
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])
size存放了数组的长度。
使用cv2.line函数进行画线的操作是需要确定起点和终点的位置坐标。数学上,角是由两条射线组成;在咱这里,简单的认为是由一个顶点往外延申出了两条线段。鼠标点击的第一个就是顶点,和点击的第二点构成第一条线,和点击的第三点构成第二条线。
不是3的倍数进入判断条件,比如1、2、4、5、7、8......
当点击次数为1、2时,round((size-1)/3)*3,得到的是0;pointsList[0]是第一个角的顶点位置。
当点击次数为4、5时,round((size-1)/3)*3,得到的是3;pointsList[0]是第二个角的顶点位置。
(python里面的索引都是从0开始往后数的)
以此类推,得到起点和终点,然后连成线。
round函数,也不是咱们数学上的四舍五入方法。比如round(0.50)输出是0,round(0.51)输出是1。
应该是大于整数的一半时,结果为那个整数;如果小于或者等于整数的一半时,结果为整数减一。
求两条直线之间的角度,需要使用斜率的方式。在编程里,斜率就是指梯度。但这个公式求出来的夹角是弧度,最后需要用math.degrees()函数转换成以度为单位的。
def gradient(pt1,pt2):
return (pt2[1]-pt1[1])/(pt2[0]-pt1[0])
就相当于第一个点的坐标为(x1,y1),第二个点的坐标为(x2,y2),斜率就是(y2-y1)/(x2-x1)。
定义一个求角度的函数:
def getAngle(pointsList):
# 将pointsList里面的数组,从右边开始往前三个进行切片
# 将pointsList最后的三个坐标给pt1,pt2,pt3
pt1, pt2, pt3 = pointsList[-3:]
# 计算第一条线段的斜率
m1 = gradient(pt1, pt2)
# 计算第二条线段的斜率
m2 = gradient(pt1, pt3)
num = (m2-m1)/(1+(m2*m1))
if num>0:
num = num
else:num = -num
angR = math.atan(num)
# math.degrees函数:用于将弧度值转换为对应的角度
angD = round(math.degrees(angR))
# 将角度值放在顶点旁边的位置
cv2.putText(img, str(angD), ((pt1[0]-10),(pt1[1])), cv2.FONT_HERSHEY_SIMPLEX,
1.5, (0, 0, 255), 2)
print(angD)
最后需要在鼠标每点击三次后就调用计算角度的函数:
if len(pointsList) % 3 == 0 and len(pointsList) !=0:
getAngle(pointsList)
结果:
钝角的度数不准确。直角也不知道是不是画的原因,导致没有90度。
回归公式,会发现,反三角函数的极限是-90和90,咱用反三角函数进行反解,当然就得不到大于或者等于90度的角了,也就得不到钝角和直角。
import cv2
import math
path = 'test.png'
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])
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)
num = (m2-m1)/(1+(m2*m1))
if num>0:
num = num
else:num = -num
angR = math.atan(num)
angD = round(math.degrees(angR))
cv2.putText(img, str(angD), ((pt1[0]-10),(pt1[1])), cv2.FONT_HERSHEY_SIMPLEX,
1.5, (0, 0, 255), 2)
print(angD)
while True:
if len(pointsList) % 3 == 0 and len(pointsList) !=0:
getAngle(pointsList)
cv2.imshow('img', img)
cv2.setMouseCallback('img',mousePoints)
if cv2.waitKey(1) & 0xFF == ord('q'):
pointsList = []
img = cv2.imread(path)
if cv2.waitKey(1) & 0xFF == 27:
break