opencv实战---角度测量

先放结论:

  • 可以学到角度测量的公式。
  • 可以学到如果按下鼠标时,就获取坐标点。

目录

相关库:

导入图片:

定义鼠标事件回调函数:

计算角度:

结果分析,发现原因:

完整代码:


接下来,就愉快地开始讲解这个项目的代码。 

相关库:

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

猜你喜欢

转载自blog.csdn.net/m0_57224196/article/details/127144193
今日推荐