【OpenCV 学习笔记】第十八章: 绘图及交互

第十八章: 绘图及交互

绘图和交互是opencv中的GUI的重要知识。

一、绘图

用opencv绘制函数绘制图像时:
  img参数都表示绘制图形的载体图形,有时也称画布、画板等。
  color参数指绘制形状的颜色,使用的是BGR颜色模型,颜色通道顺序是BGR。
  thickness参数表示线条的粗细,默认值是1,如果设置为-1,表示填充图形,就是绘制的图形是实心的。
  lineType参数表示线条的类型。
  shift参数表示精度,比如圆心坐标的精度,一般情况下这个参数不需要设置。

  • 绘制直线
    img = cv2.line(img, pt1, pt2, color[, thickness[, lineType]])
    pt1,pt2分别是线段的起点和终点。注意:pt1,pt2都是用(x,y)的形式表示的,就是一个点的横纵坐标,但是这个坐标系和图形的行列标是正好相反的,就是:(x,y)--(列标,行标)

  • 绘制矩形
    img = cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType]])
    pt1,pt2分别是矩形的左上顶点和右下顶点。

  • 绘制圆形
    img = cv2.circle(img, center, radius, color[, thickness[, lineType]])

  • 绘制椭圆
    img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color[, thickness[, lineType]])
    center:椭圆的圆心坐标;axes:轴的长度;angle:是偏转的角度;startAngle:圆弧起始角的角度;endAngle:圆弧终结角的角度

  • 绘制多边形
    img = cv2.polylines(img, pts, isClosed, color[, thickness[, lineType[, shift]]])
    pts:多边形的各个顶点;siClosed:是否封闭,当该参数=True时,将最后一个点和第一个点连接,让多边形闭合,否则,仅仅将各点依次连接起来构成一条曲线。

  • 在图像上绘制文字
    text = cv2.putText(img, text, org, fontFace, fontScale, color[, thickness[, lineType[, bottomLeftOrigin]]])
    text:要绘制的字的内容;org:绘制的位置,以文字的左下角为起点;fontFace:字体类型;fontScale:字体大小;bottomLeftOrigin:控制文字方向,默认是False,当这个参数=True时,文字是垂直镜像的效果。

    #例18.1 绘制各种形状  
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    img = np.zeros((301,301,3), np.uint8)
    
    #-----------绘制直线---------------------------#注意坐标点的表示,点用(列标,行标)表示
    img1 = img.copy()
    img1 = cv2.line(img1, (0,0), (300, 300), (255,0,0), 3)
    img1 = cv2.line(img1, (0,100), (300,100), (0,255,0), 1)    
    img1 = cv2.line(img1, (100,0), (100,300), (0,0,255), 6)
    
    #-----------绘制矩形------------------------------------
    img2 = img.copy()
    img2 = cv2.rectangle(img2, (50,50), (200,250), (0,0,255), -1)
    
    #----------绘制圆形------------------------------------
    img3 = img.copy()
    for r in range(5, 150, 15):   #绘制一组同心圆
        img3 = cv2.circle(img3, (150,150), r, (255,255,255), 3)
    
    img4 = img.copy()
    for i in range(0, 100):   #随机生成大小位置都随机的100个实心圆
        x = np.random.randint(0,301)
        y = np.random.randint(0,301)
        r = np.random.randint(0, 20)
        color = np.random.randint(0, 256, size=(3)).tolist()
        img4 = cv2.circle(img4, (x,y), r, color, -1)
        
    #----------随机绘制一组同心椭圆------------------------------------
    img5 = img.copy()
    for i in range(0,20):
        angle = np.random.randint(0,361)
        color = np.random.randint(0,256,size=(3)).tolist()
        cv2.ellipse(img5, (150,150), (70,150), angle, 0, 360, color)
    
    #----------绘制多边形------------------------------------
    img6 = img.copy()
    pts1 = np.array([[50,100],[100,220],[220,220],[240,80]]).reshape(-1, 1,2)  #多边形的顶点坐标一定要是一个(顶点个数,1,2)的一个数组,而且这个数组的数据类型必须为np.int32的。
    img6 = cv2.polylines(img6, [pts1], False, (0,255,0),2)   #注意参数pts和控制是否闭合的第3个参数
    
    pts2 = np.array([[40,90],[90,230],[230,230],[250,70]]).reshape(-1, 1,2)
    img6 = cv2.polylines(img6, [pts2], True, (0,0,255),2)
    
    #----------添加文字------------------------------------
    img7 = img.copy()
    img7 = cv2.putText(img7, 'OpenCV', (50,100), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,255,0), 6) #参数依次是:画布、文字内容、文字的左下角坐标、字体、字的大小,字的颜色,字的粗细
    img7 = cv2.putText(img7, 'OpenCV', (50,120), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 2)
    img7 = cv2.putText(img7, 'OpenCV', (50,200), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 6, cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, True)   #镜像文字效果
    
    #可视化:
    plt.figure(figsize=(20,10))
    plt.subplot(181), plt.imshow(img1[:,:,::-1])   
    plt.subplot(182), plt.imshow(img2[:,:,::-1]) 
    plt.subplot(183), plt.imshow(img3[:,:,::-1])  
    plt.subplot(184), plt.imshow(img4[:,:,::-1])  
    plt.subplot(185), plt.imshow(img5[:,:,::-1])  
    plt.subplot(186), plt.imshow(img6[:,:,::-1])  
    plt.subplot(187), plt.imshow(img7[:,:,::-1])  
    
    plt.show()

    二、鼠标交互

    鼠标交互就是:opencv支持,在用opencv生成的桌面窗口里,用户在窗口上操作鼠标,也就是用户触发鼠标事件,这个窗口可以对用户的动作做出特定的响应。也就是通过鼠标的人机交互。比如,在窗口上,用户希望按一下鼠标左键就画一条直线,双击一下鼠标左键就出现一个圆,或者鼠标在窗口一移动就打印出鼠标的坐标等操作。

  • 鼠标响应函数:def OnMouseAction(event, x, y, flags, param)
    这个函数的函数名可以随便取,但是参数必须包含上面5个参数
    event表示鼠标事件,比如鼠标移动、单击左键、右键等事件,比如,EVENT_MOUSEMOVE是鼠标移动事件,事件也可以用数字0表示,C++里面都是用数字表示。比如,EVENT_LBUTTONDOWN表示按下鼠标左键,就也可以用1表示。。。。。等等
    x,y是发生鼠标事件的坐标点
    flags是鼠标的组合按键,或者说是鼠标的组合操作,比如:EVENT_FLAG_LBUTTON按下左键,用1表示,EVENT_FLAG_CRTLKEY按下ctrl键和鼠标,也可以用8表示,,,等等。
    param为函数ID,标识所响应的事件函数,相当于自定义一个OnMouseAction()函数的ID。或者说是用户传给cv2.setMouseCallback()的userdata参数,cv2.setMouseCallback()函数再把这个参数值传给OnMouseAction()函数的param参数。

  • 实现鼠标交互的API是:cv2.setMouseCallback(winname, OnMouseAction, userdata)
    这个函数也叫回调函数,就是给鼠标设置一个回调,意思就是一旦鼠标在窗口winname里做一个动作,比如单击左键,就要去执行OnMouseAction函数,而OnMouseAction函数就是定义鼠标事件对应的特定响应。所以这个API的参数OnMouseAction不是一个变量而是一个函数。
    winname:是窗口的名字
    OnMouseAction:是鼠标响应函数
    userdata:是用户输入的外部参数。这个参数值会传给OnMouseAction()函数中的param参数。

    小结:要实现人机通过鼠标交互:
      第一,先要编写一个鼠标响应函数。这个函数的功能是:假设用户已经在显示器某个特定窗口触发了鼠标事件,这个函数可以返回一些参数和我们用户希望的结果。这个函数是我们自己定义的,比如def OnMouseAction()。
      第二,编写回调函数。就是把第一步定义的函数和一个特定的窗口绑定,或者说是和某个特定的窗口建立联系,这样用户在这个特定窗口内触发鼠标事件时,这个窗口就能找到响应函数并执行。这样用户就能在电脑显示器屏幕上某个特定窗口里进行人机交互了。这个功能通过回调函数cv2.setMouseCallback()来实现。

  • 查看opencv所支持的鼠标事件
    events = [i for i in dir(cv2) if 'EVENT' in i]
    print(events)
    ##例18.2 熟悉鼠标交互函数的各个参数
    #说明:
    #本代码的功能是:生成一个新窗口,窗口展示一张黑图片,在这个窗口做任何的鼠标操作,就调用鼠标回调函数setMouseCallback(),执行这个函数就调用我们自定义的鼠标响应函数mouse_callback()
    #然后打印出鼠标响应函数的各个参数,观察各个参数的含义
    
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    def mouse_callback(event, x, y, flags, userdata):  #定义鼠标响应函数,这个函数名可以随便取,但是5个位置参数不能随便写,它不是关键字参数,所以要知道每个参数的含义
        print(event, x, y, flags, userdata)  #把这个函数的参数打印出来观察一下这些参数的含义  A
        if event ==2:    #再执行一个功能,当按下鼠标右键就打印一串字符aaaa。按下鼠标右键的事件是EVENT_RBUTTONDOWN,数字表示是2
            print('aaaaa')
    
    cv2.namedWindow('mouse', cv2.WINDOW_NORMAL)  #创建窗口
    cv2.resizeWindow('mouse', 640, 360)  #调整窗口大小
    img = np.zeros((360, 640, 3), np.uint8)  #生成一个全黑的图片,注意:这个图片的大小应该和窗口的大小一致,所以行列要交互位置!
    
    cv2.setMouseCallback('mouse', mouse_callback, '123')  #设置鼠标的回调函数,参数'123'是用户传入的用户数据,这个用户数据就会被传到mouse_callback的参数userdata里面。  
    
    while True:   #一直循环去展示img这个图片
        cv2.imshow('mouse', img)  #展示img这个图片
        key = cv2.waitKey(1)   #图片显示1毫秒就可以退出了,我们接受退出返回的key
        if key & 0xFF ==ord('q'):  #如果key是q的ascii码就break
            break
    cv2.destroyAllWindows()
    0 0 229 0 123
    0 2 229 0 123
    0 3 229 0 123
    0 4 228 0 123
    0 6 228 0 123
    0 7 227 0 123
    0 8 227 0 123
    0 9 227 0 123
    0 10 227 0 123
    2 10 227 2 123
    aaaaa
    5 10 227 0 123
    0 10 227 0 123
    0 9 227 0 123
    0 8 227 0 123
    0 7 228 0 123
    0 4 230 0 123
    

    A:运行上面cell里面的代码,就会出现一个黑色的窗口,我们鼠标只要接触这个黑色窗口就开始打印鼠标事件。比如鼠标在窗口移动就触发鼠标移动事件,鼠标移动事件就被打印出来了,所以运行结果里面的第一个数字0就表示鼠标移动,假如我们还点击了鼠标左键(这里没操作是因为我们的waitkey设置的是1毫秒,就是每间隔1毫秒就监控一次鼠标,所以瞬间就会打印很多的鼠标信息,页面很快就被占满了,不方面显示)第一个数字就会是1,1表示按下鼠标左键,还会显示数字4,4表示左键释放。由于我们监控的时间极短,所以鼠标的任何动作事件都被打印出来了。
    后面的第二个第三个数字表示鼠标的坐标;
    第四个数字表示flags=0,表示没有组合事件;如果我们敲键盘上的shift键,这个数就会打印16,16表示按shift键;如果我们同时按shift键+鼠标左键,这个数就会是17,表示16+1,表示shift+左键。
    第五个数字123就是我们用户传给回调函数的数据。

    #例18.3 设计一个程序,对触发的鼠标事件进行判断  
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    #--------定义回调函数---------------------
    def MouseAction(event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDBLCLK:  #双击左键,注意:这个事件要放在单击左键的前面,否则没法执行这个代码块
            px = np.random.randint(1,200)
            py = np.random.randint(1,200)
            color = np.random.randint(0,256, size=(2)).tolist()
            cv2.rectangle(img, (x,y), (px,py), color, 2)
        elif event == cv2.EVENT_LBUTTONDOWN: 
            print('您单击了鼠标左键')
        elif event == cv2.EVENT_RBUTTONDOWN:
            print('您单击了鼠标右键')
        elif flags == cv2.EVENT_FLAG_LBUTTON:   #注意:这里是赋值的是flags,因为是组合事件
            print('您按住左键拖动了鼠标')
        elif event == cv2.EVENT_MBUTTONDOWN:
            print('您单击了鼠标中间键')
            cv2.circle(img, (x,y), 100, 125)
    
    #-------将回调函数和特定窗口绑定--------------
    img = np.ones((600,600), np.uint8)*255
    cv2.namedWindow('mouse action')
    cv2.setMouseCallback('mouse action', MouseAction)
    while True:
        cv2.imshow('mouse action', img)
        if cv2.waitKey(10)==97:    #97是小写a的ascii码,27是esc的ascii码
            break
    cv2.destroyAllWindows()
    您单击了鼠标中间键
    您单击了鼠标左键
    您按住左键拖动了鼠标
    您按住左键拖动了鼠标
    您按住左键拖动了鼠标
    
    #例18.4 设计一个交互程序,运用waitKey函数来实现交互,即通过键盘和鼠标的组合控制显示不同的形状或文字  
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    thickness=-1
    mode=1
    d=400
    
    def draw_circle(event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDOWN:  #单击左键
            a = np.random.randint(0, d-50)
            r = np.random.randint(0, d/5)
            angle = np.random.randint(0,361)
            color = np.random.randint(0, 256, size=(3)).tolist()
            if mode==1:
                cv2.rectangle(img, (x,y), (a,a), color, thickness)
            elif mode==2:
                cv2.circle(img, (x,y), r, color, thickness)
            elif mode==3:
                cv2.line(img, (a,a), (x,y), color, 3)
            elif mode==4:
                cv2.ellipse(img, (x,y), (100, 150), angle, 0, 360, color, thickness)
            elif mode==5:
                cv2.putText(img, 'niuniu', (round(x),round(y)), cv2.FONT_HERSHEY_SIMPLEX, 2, color, 5)
    
    img = np.ones((d,d,3), np.uint8)*255
    cv2.namedWindow('image')
    cv2.setMouseCallback('image', draw_circle)
    while True:
        cv2.imshow('image', img)
        k=cv2.waitKey(1)&0xFF     #用waitKey()函数来设计交互  
        if k==ord('r'):
            mode=1
        elif k==ord('c'):
            mode=2
        elif k==ord('l'):
            mode=3
        elif k==ord('e'):
            mode=4
        elif k==ord('t'):
            mode=5
        elif k==ord('f'):
            thickness=-1
        elif k==ord('u'):
            thickness=3
        elif k==27:   #esc键
            break
    cv2.destroyAllWindows()

    三、滚动条控件交互

    滚动条TrackBar,是opencv中非常方便的交互工具,可以通过调节滚动条来设置、获取特定范围内的特定值。 滚动条也是依附特定的窗口而存在。

  • 创建滚动条控件:cv2.createTrackbar(trackbarname, winname, value, count, function)
    trackbarname:滚动条的名称
    winname:滚动条所依附的窗口的名称
    value:滚动条的最小值,也是滚动条的默认初始值
    count:滚动条的最大值
    function:滚动条改变后要实现的操作,就是滚动条函数要调用的函数。这个function是需要我们自己定义的一个函数,这个函数的功能是帮我们实现一些我们想要的操作。

  • 获取滚动条的当前值:ret = getTrackbarPos(trackbarname, winname)
    trackbarname:上面函数生成的滚动条的名称
    winname:上面函数生成的滚动条所依附的窗口

    #例18.5 通过滚动条控制三原色,模拟调色板效果  
    import cv2
    import numpy as np
    
    def changeColor(x):
        r = cv2.getTrackbarPos('Rbar', 'colorchange')
        g = cv2.getTrackbarPos('Gbar', 'colorchange')
        b = cv2.getTrackbarPos('Bbar', 'colorchange')
        img[:] = [b,g,r]  #将Img的三个通道的值替换为bgr,[]表示替换原来的值
        print(x)
        
    img = np.zeros((500, 500, 3), np.uint8)
    cv2.namedWindow('colorchange')
    cv2.createTrackbar('Rbar', 'colorchange', 0, 255, changeColor)
    cv2.createTrackbar('Gbar', 'colorchange', 0, 255, changeColor)
    cv2.createTrackbar('Bbar', 'colorchange', 0, 255, changeColor)
    while True:
        cv2.imshow('colorchange', img)
        k = cv2.waitKey(1)
        if k==27:  #esc键
            break
    cv2.destroyAllWindows()
    #例18.6 用滚动条控制阈值处理参数  
    import cv2
    ttype=0
    value=0
    
    def f_type(x):
        ttype = cv2.getTrackbarPos('t_type', 'lena')
        value = cv2.getTrackbarPos('t_value', 'lena')
        ret, o = cv2.threshold(img, value, 255, ttype)
        cv2.imshow('lena', o)
    
    def f_value(x):
        ttype = cv2.getTrackbarPos('t_type', 'lena')
        value = cv2.getTrackbarPos('t_value', 'lena')
        ret, o = cv2.threshold(img, value, 255, ttype)
        cv2.imshow('lena', o)
        
    img = cv2.imread(r'C:\Users\25584\Desktop\lena.bmp',0)
    cv2.namedWindow('lena')
    cv2.imshow('lena', img)
    cv2.createTrackbar('t_type', 'lena', 0,4, f_type)
    cv2.createTrackbar('t_value', 'lena', 0, 255, f_value)
    
    if cv2.waitKey(0) == 27:
        cv2.destroyAllWindows()
    #例18.7 用滚动条作为开关使用  
    import cv2
    import numpy as np
    
    thickness=-1
    
    def kaiguan(x):
        pass
    def draw(event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDBLCLK:
            px = x
            py = y
            r = np.random.randint(1, 100)
            color = np.random.randint(0, 256, size=(3)).tolist()
            cv2.circle(img, (x,y), r, color, thickness)
    
    img = np.ones((500, 500, 3), np.uint8)*255
    cv2.namedWindow('hua ban')
    cv2.setMouseCallback('hua ban', draw)
    cv2.createTrackbar('bar', 'hua ban', 0, 1, kaiguan)
    
    while True:
        cv2.imshow('hua ban', img)
        k = cv2.waitKey(1)
        g = cv2.getTrackbarPos('bar', 'hua ban')
        if g == 0:
            thickness = -1
        else:
            thickness = 2
        if k==27:
            break
    cv2.destroyAllWindows()

你可能感兴趣的:(opencv,学习,计算机视觉)