鼠标作为画笔

用鼠标绘制图形是很有用的,我们在后面的目标跟踪中会使用鼠标将需要跟踪的目标标注出来,之后对目标进行实时跟踪,所以今天我们来了解一下OpenCV中的一些基础知识—鼠标画笔。

首先需要了解一个函数:

cv2.setMouseCallback(windowName, MouseCallback , param=None)

其各个参数为:

windowName:窗口名称

MouseCallback:鼠标响应回调函数

param:响应函数传递的的参数

在这里,我们创建一个简单的应用程序,无论我们在哪里双击它,都可以在图像上绘制一个圆。

首先,我们创建一个鼠标回调函数,该函数在发生鼠标事件时执行。该函数原型为(此函数为我们自己所定义,OpenCV库中是没有此函数的,名称可以自己任意定义):

MouseCallback (int event, int x, int y, int flags, void *userdata)

其各个部分参数为:

event:一个MouseEventTypes 常量

x:鼠标的x坐标

y:鼠标的y坐标

flags:一个MouseEventFlags常量

userdata:可选参数

鼠标事件可以是与鼠标相关的任何东西,例如左键按下,右键按下,左键双击等。它为我们提供了每个鼠标事件的坐标(x,y)。 通过此活动和地点,我们可以做任何我们喜欢的事情。 要列出所有可用的可用参数,我们在pycharm终端中运行以下代码:


import cv2 as cv

events = [i for i in dir(cv) if 'EVENT' in i]
print( events )

我们查看输出,会发现一大堆参数名称:

['EVENT_FLAG_ALTKEY', 'EVENT_FLAG_CTRLKEY', 'EVENT_FLAG_LBUTTON', 'EVENT_FLAG_MBUTTON', 'EVENT_FLAG_RBUTTON', 'EVENT_FLAG_SHIFTKEY', 'EVENT_LBUTTONDBLCLK', 'EVENT_LBUTTONDOWN', 'EVENT_LBUTTONUP', 'EVENT_MBUTTONDBLCLK', 'EVENT_MBUTTONDOWN', 'EVENT_MBUTTONUP', 'EVENT_MOUSEHWHEEL', 'EVENT_MOUSEMOVE', 'EVENT_MOUSEWHEEL', 'EVENT_RBUTTONDBLCLK', 'EVENT_RBUTTONDOWN', 'EVENT_RBUTTONUP']

现在我们来看一下MouseCallback 函数中的event参数,其代表一个MouseEventFlags常量:

MouseEventFlags

  cv.EVENT_FLAG_LBUTTON = 1, 左键拖拽

  cv.EVENT_FLAG_RBUTTON = 2, 右键拖拽

  cv.EVENT_FLAG_MBUTTON = 4, 中键不放

  cv.EVENT_FLAG_CTRLKEY = 8,按住ctrl不放

  cv.EVENT_FLAG_SHIFTKEY = 16, 按住shift不放

  cv.EVENT_FLAG_ALTKEY = 32 ,按住alt不放

继续看MouseCallback 函数中的flags参数,其代表一个MouseEventFlags常量:

cv.MouseEventTypes

  cv.EVENT_MOUSEMOVE = 0, 鼠标移动

  cv.EVENT_LBUTTONDOWN = 1, 左键按下

  cv.EVENT_RBUTTONDOWN = 2, 右键按下

  cv.EVENT_MBUTTONDOWN = 3, 中键按下

  cv.EVENT_LBUTTONUP = 4, 左键释放

  cv.EVENT_RBUTTONUP = 5, 右键释放

  cv.EVENT_MBUTTONUP = 6, 中键释放

  cv.EVENT_LBUTTONDBLCLK = 7, 左键双击

  cv.EVENT_RBUTTONDBLCLK = 8, 右键双击

  cv.EVENT_MBUTTONDBLCLK = 9, 中健双击

  cv.EVENT_MOUSEWHEEL = 10, 滚轮滑动

  cv.EVENT_MOUSEHWHEEL = 11 横向滚轮滑动

接下来我们可以开始进行实验了,创建鼠标回调函数具有特定的格式,该格式在所有地方都是相同的。它仅在功能上有所不同。因此,现在我们用鼠标回调函数做一件事,在我们双击的地方绘制一个圆圈:

import numpy as np
import cv2 as cv


def draw_circle(event, x, y, flags, param):
    if event == cv.EVENT_LBUTTONDBLCLK:
        cv.circle(img, (x, y), 100, (255, 0, 0), -1)


img = np.zeros((512, 512, 3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image', draw_circle)
while (1):
    cv.imshow('image', img)
    if cv.waitKey(20) & 0xFF == 27:
        break
cv.destroyAllWindows()

这里面我们调用的参数是EVENT_LBUTTONDBLCLK,意思就是鼠标双击触发。我们双击任意一个区域,都会以此为圆心创建一个圆形出来:

image.png

鼠标画图形

现在我们需要进行更高级的操作, 在这种情况下,我们像在“画图”应用程序中一样,通过拖动鼠标来绘制矩形或圆形(取决于我们选择的模式)。 因此,我们的鼠标回调函数有两个部分,一个用于绘制矩形,另一个用于绘制圆形。这个将会非常有帮助,后期进行手动目标跟踪的时候,我们就会用鼠标自己手动标注这些图形,从而完成实时的目标跟踪。

现在我们来实现一个综合的例子,这个实例会帮助你理解图像交互的一些思想:

在图像上用鼠标画图,可以画圆或矩形,按q键在两种模式下切换。左键按下时开始画图,移动到哪儿画到哪儿,左键释放时结束画图。听上去很复杂,是吗?一步步分析下:

· 用鼠标画图:需要定义鼠标的回调函数mouse_event

· 画圆或矩形:需要定义一个画图的模式mode

· 左键单击、移动、释放:需要捕获三个不同的事件

· 开始画图,结束画图:需要定义一个画图的标记位drawing

好,开始代码:

import numpy as np
import cv2 as cv

drawing = False  # true if mouse is pressed
mode = True  # if True, draw rectangle. Press 'm' to toggle to curve
ix, iy = -1, -1


# mouse callback function
def draw_circle(event, x, y, flags, param):
    global ix, iy, drawing, mode
    if event == cv.EVENT_LBUTTONDOWN:
        drawing = True
        ix, iy = x, y
    elif event == cv.EVENT_MOUSEMOVE:
        if drawing:
            if mode:
                cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1)
            else:
                cv.circle(img, (x, y), 5, (0, 0, 255), -1)
    elif event == cv.EVENT_LBUTTONUP:
        drawing = False
        if mode:
            cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), -1)
        else:
            cv.circle(img, (x, y), 5, (0, 0, 255), -1)


img = np.zeros((512, 512, 3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image', draw_circle)
while 1:
    cv.imshow('image', img)
    k = cv.waitKey(1) & 0xFF
    if k == ord('q'):
        mode = not mode
    elif k == 27:
        break
cv.destroyAllWindows()

效果:

image.png

ok,搞定。

但是假如我们想创建一个未填充的矩形怎么办呢?

那么我们需要修改一下代码,在上一节中就已经讲过,画矩形函数以及画圆函数等等函数,它们的最后一个参数当为-1时,代表为实心,当大于0时,即为空心,假如我们定义为1,那么画出的图形的轮廓的粗细程度就为1,我们来看代码:

import numpy as np
import cv2 as cv

drawing = False  # true if mouse is pressed
mode = True  # if True, draw rectangle. Press 'm' to toggle to curve
ix, iy = -1, -1


# mouse callback function
def draw_circle(event, x, y, flags, param):
    global ix, iy, drawing, mode
    if event == cv.EVENT_LBUTTONDOWN:
        drawing = True
        ix, iy = x, y
    elif event == cv.EVENT_MOUSEMOVE:
        if drawing:
            if mode:
                tmp = img.copy()
                cv.rectangle(tmp, (ix, iy), (x, y), (0, 255, 0), 1)
                cv.imshow('image', tmp)
    elif event == cv.EVENT_LBUTTONUP:
        drawing = False
        if mode:
             cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), 1)


img = np.zeros((512, 512, 3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image', draw_circle)
cv.imshow("image",img)
while 1:
    k = cv.waitKey(1) & 0xFF
    if k == 27:
        break
cv.destroyAllWindows()

我们看一下效果:

image.png

现在我们还可以修改一下代码,我们将EVENT_LBUTTONUP的参数语句执行的功能取消,这样的话,我们在画下一个矩形时上一个就会被自动清除:

import numpy as np
import cv2 as cv

drawing = False  # true if mouse is pressed
mode = True  # if True, draw rectangle. Press 'm' to toggle to curve
ix, iy = -1, -1


# mouse callback function
def draw_circle(event, x, y, flags, param):
    global ix, iy, drawing, mode
    if event == cv.EVENT_LBUTTONDOWN:
        drawing = True
        ix, iy = x, y
    elif event == cv.EVENT_MOUSEMOVE:
        if drawing:
            if mode:
                tmp = img.copy()
                cv.rectangle(tmp, (ix, iy), (x, y), (0, 255, 0), 1)
                cv.imshow('image', tmp)
    elif event == cv.EVENT_LBUTTONUP:
        drawing = False
        # if mode:
        #      cv.rectangle(img, (ix, iy), (x, y), (0, 255, 0), 1)


img = np.zeros((512, 512, 3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image', draw_circle)
cv.imshow("image",img)
while 1:
    k = cv.waitKey(1) & 0xFF
    if k == 27:
        break
cv.destroyAllWindows()

image.png

至此,OpenCV的鼠标绘图部分就算结束了,大家要想熟练掌握,还是自己多多练习

posted @ 2021-12-03 15:00  wuyuan2011woaini  阅读(359)  评论(0编辑  收藏  举报