zoukankan      html  css  js  c++  java
  • opencv(5)GUI

    OpenCV的图形用户界面(Graphical User Interface, GUI)和绘图等相关功能也是很有用的功能,无论是可视化,图像调试还是我们这节要实现的标注任务,都可以有所帮助。

    窗口循环

    OpenCV显示一幅图片的函数是cv2.imshow(),第一个参数是显示图片的窗口名称,第二个参数是图片的array。不过如果直接执行这个函数的话,什么都不会发生,因为这个函数得配合cv2.waitKey()一起使用。cv2.waitKey()指定当前的窗口显示要持续的毫秒数,比如cv2.waitKey(1000)就是显示一秒,然后窗口就关闭了。比较特殊的是cv2.waitKey(0),并不是显示0毫秒的意思,而是一直显示,直到有键盘上的按键被按下,或者鼠标点击了窗口的小叉子才关闭。cv2.waitKey()的默认参数就是0,所以对于图像展示的场景,cv2.waitKey()或者cv2.waitKey(0)是最常用的。

    cv2.waitKey()参数不为零的时候则可以和循环结合产生动态画面。Python的itertools模块中的cycle函数可以把一个可遍历结构编程一个无限循环的迭代器。cv2.waitKey()返回的就是键盘上出发的按键。对于字母就是ascii码,特殊按键比如上下左右等,则对应特殊的值,其实这就是键盘事件的最基本用法。

    鼠标和键盘事件

    cv2.waitKey()就是获取键盘消息的最基本方法。下面这段循环代码就能够获取键盘上按下的按键,并在终端输出:

    while key != 27:
        cv2.imshow('Honeymoon Island', img)
        key = cv2.waitKey()
        # 如果获取的键值小于256则作为ascii码输出对应字符,否则直接输出值
        msg = '{} is pressed'.format(chr(key) if key < 256 else key)
        print(msg)

    通过这个程序我们能获取一些常用特殊按键的值。

    需要注意的是在不同的操作系统里这些值可能是不一样的。鼠标事件比起键盘事件稍微复杂一点点,需要定义一个回调函数,然后把回调函数和一个指定名称的窗口绑定,这样只要鼠标位于画面区域内的事件就都能捕捉到。把下面这段代码插入到上段代码的while之前,就能获取当前鼠标的位置和动作并输出:

    # 定义鼠标事件回调函数
    def on_mouse(event, x, y, flags, param):
    
        # 鼠标左键按下,抬起,双击
        if event == cv2.EVENT_LBUTTONDOWN:
            print('Left button down at ({}, {})'.format(x, y))
        elif event == cv2.EVENT_LBUTTONUP:
            print('Left button up at ({}, {})'.format(x, y))
        elif event == cv2.EVENT_LBUTTONDBLCLK:
            print('Left button double clicked at ({}, {})'.format(x, y))
    
        # 鼠标右键按下,抬起,双击
        elif event == cv2.EVENT_RBUTTONDOWN:
            print('Right button down at ({}, {})'.format(x, y))
        elif event == cv2.EVENT_RBUTTONUP:
            print('Right button up at ({}, {})'.format(x, y))
        elif event == cv2.EVENT_RBUTTONDBLCLK:
            print('Right button double clicked at ({}, {})'.format(x, y))
    
        # 鼠标中/滚轮键(如果有的话)按下,抬起,双击
        elif event == cv2.EVENT_MBUTTONDOWN:
            print('Middle button down at ({}, {})'.format(x, y))
        elif event == cv2.EVENT_MBUTTONUP:
            print('Middle button up at ({}, {})'.format(x, y))
        elif event == cv2.EVENT_MBUTTONDBLCLK:
            print('Middle button double clicked at ({}, {})'.format(x, y))
    
        # 鼠标移动
        elif event == cv2.EVENT_MOUSEMOVE:
            print('Moving at ({}, {})'.format(x, y))
    
    # 为指定的窗口绑定自定义的回调函数
    cv2.namedWindow('Honeymoon Island')
    cv2.setMouseCallback('Honeymoon Island', on_mouse)

    标注小工具

    基本思路是对要标注的图像建立一个窗口循环,然后每次循环的时候对图像进行一次拷贝。鼠标在画面上画框的操作,以及已经画好的框的相关信息在全局变量中保存,并且在每个循环中根据这些信息,在拷贝的图像上再画一遍,然后显示这份拷贝的图像。

    基于这种实现思路,使用上我们采用一个尽量简化的设计:

    - 输入是一个文件夹,下面包含了所有要标注物体框的图片。如果图片中标注了物体,则生成一个相同名称加额外后缀名的文件保存标注信息。

    - 标注的方式是按下鼠标左键选择物体框的左上角,松开鼠标左键选择物体框的右下角,鼠标右键删除上一个标注好的物体框。所有待标注物体的类别,和标注框颜色由用户自定义,如果没有定义则默认只标注一种物体,定义该物体名称叫“Object”。

    - 方向键的←和→用来遍历图片,↑和↓用来选择当前要标注的物体,Delete键删除一张图片和对应的标注信息。

    每张图片的标注信息,以及自定义标注物体和颜色的信息,用一个元组表示,第一个元素是物体名字,第二个元素是代表BGR颜色的tuple或者是代表标注框坐标的元组。对于这种并不复杂复杂的数据结构,我们直接利用Python的repr()函数,把数据结构保存成机器可读的字符串放到文件里,读取的时候用eval()函数就能直接获得数据。这样的方便之处在于不需要单独写个格式解析器。如果需要可以在此基础上再编写一个转换工具就能够转换成常见的Pascal VOC的标注格式或是其他的自定义格式。

    在这些思路和设计下,我们定义标注信息文件的格式的例子如下:

    ('Hill', ((221, 163), (741, 291)))
    ('Horse', ((465, 430), (613, 570)))
    

    元组中第一项是物体名称,第二项是标注框左上角和右下角的坐标。这里之所以不把标注信息的数据直接用pickle保存,是因为数据本身不会很复杂,直接保存还有更好的可读性。自定义标注物体和对应标注框颜色的格式也类似,不过更简单些,因为括号可以不写,具体如下:

    'Horse', (255, 255, 0)
    'Hill', (0, 255, 255)
    'DiaoSi', (0, 0, 255)
    

    第一项是物体名称,第二项是物体框的颜色。使用的时候把自己定义好的内容放到一个文本里,然后保存成和待标注文件夹同名,后缀名为labels的文件。比如我们在一个叫samples的文件夹下放上一些草原的照片,然后自定义一个samples.labels的文本文件。把上段代码的内容放进去,就定义了小山头的框为黄色,骏马的框为青色,以及红色的屌丝。

  • 相关阅读:
    排序简介
    排序集锦
    排序图解
    UVA 11809
    uva1588kickdown
    C++中输入输出的重定向
    uva1587BOX
    uva 10340 All in All
    uva 1368 DNA Consensus String
    彻底理解MapReduce shuffle过程原理
  • 原文地址:https://www.cnblogs.com/qjoanven/p/7680330.html
Copyright © 2011-2022 走看看