• OpenCV-Python setMouseCallback回调函数中图像变量img的传递方法解析


    ☞ ░ 前往老猿Python博文目录

    一、使用全局变量进行变量传递

    OpenCV-Python中可以使用setMouseCallback来设置鼠标事件的回调函数,我们来看个样例。

    1.1、案例1代码

    import numpy as np
    import cv2 #freetype
    
    mode = 0
    
    #创建回调函数
    def OnMouseAction(event,x,y,flags,param):
        global x1, y1
        img = img1
    
        if mode == 0 and event == cv2.EVENT_LBUTTONDOWN:
            print("左键点击")
            cv2.line(img,(0,0),(x,y),(255,255,0),2)
    
        if mode == 1 and event == cv2.EVENT_LBUTTONDOWN:
            print("左键点击1")
            x1, y1 = x, y
        elif mode == 1 and event==cv2.EVENT_MOUSEMOVE and flags ==cv2.EVENT_FLAG_LBUTTON:
            print("左鍵拖曳1")
            cv2.rectangle(img2,(x1,y1),(x,y),(0,255,0),-1)
    
    
    
    img1 = np.zeros((500,500,3),np.uint8)
    img2= np.zeros((500,500,3),np.uint8)
    cv2.putText(img1,'1',(200,200),cv2.FONT_HERSHEY_SIMPLEX, 4, (255,0,0))
    cv2.putText(img2,'2',(200,200),cv2.FONT_HERSHEY_SIMPLEX, 4, (255,0,0))
    cv2.namedWindow('image1')
    cv2.namedWindow('image2')
    cv2.setMouseCallback('image1',OnMouseAction)
    
    while(1):
        cv2.imshow('image1',img1)
        cv2.imshow('image2', img2)
        cbf()
        k=cv2.waitKey(1)
        if k==ord('l'):
            mode = 0
        elif k==ord('t'):
            mode = 1
        elif k==ord('q'):
            break
    cv2.destroyAllWindows()
    

    1.2、案例1程序功能及运行截图

    上面的代码创建了两个图像窗口,在窗口image1中设置了回调函数,当开始运行时,直接在窗口1中画线,当输入‘t’键切换模式后,在窗口中的拖拽却在窗口2中画填充的矩形。运行截图:
    在这里插入图片描述

    1.3、简析

    回调函数OnMouseAction中使用了两个全局变量img1、img2,因此回调函数中对图像的操作是通过全局变量来实现的。关于该使用方法请参考《第5.4节 Python函数中的变量及作用域》的介绍。

    二、使用回调函数参数param进行变量传递

    当回调函数要操作的对象不是全局变量时,回调函数就不能使用第一种方法传递变量,此时只能通过回调函数参数param进行变量传递。

    2.1、案例2:将案例1中的img1传值改成param进行传递

    import numpy as np
    import cv2 #freetype
    
    mode = 0
    
    #创建回调函数
    def OnMouseAction(event,x,y,flags,param):
        global x1, y1
        img = param
    
        if mode == 0 and event == cv2.EVENT_LBUTTONDOWN:
            print("左键点击")
            cv2.line(img,(0,0),(x,y),(255,255,0),2)
    
        if mode == 1 and event == cv2.EVENT_LBUTTONDOWN:
            print("左键点击1")
            x1, y1 = x, y
        elif mode == 1 and event==cv2.EVENT_MOUSEMOVE and flags ==cv2.EVENT_FLAG_LBUTTON:
            print("左鍵拖曳1")
            cv2.rectangle(img2,(x1,y1),(x,y),(0,255,0),-1)
    
    
    
    img1 = np.zeros((500,500,3),np.uint8)
    img2= np.zeros((500,500,3),np.uint8)
    cv2.putText(img1,'1',(200,200),cv2.FONT_HERSHEY_SIMPLEX, 4, (255,0,0))
    cv2.putText(img2,'2',(200,200),cv2.FONT_HERSHEY_SIMPLEX, 4, (255,0,0))
    cv2.namedWindow('image1')
    cv2.namedWindow('image2')
    cv2.setMouseCallback('image1',OnMouseAction,img1)
    
    while(1):
        cv2.imshow('image1',img1)
        cv2.imshow('image2', img2)
    
        k=cv2.waitKey(1)
        if k==ord('l'):
            mode = 0
        elif k==ord('t'):
            mode = 1
        elif k==ord('q'):
            break
    cv2.destroyAllWindows()
    

    上面的代码和案例1的代码相比,只修改了第11行和32行的代码,但程序功能完全相同,这证明了param传值的可行性。

    2.2、案例3

    # -*- coding: utf-8 -*-
    
    import cv2 
    def OnMouseLBUTTONDOWN(event,x,y,flags,param):
        frame = param
        if frame is not None:
            if event == cv2.EVENT_LBUTTONDOWN:
                cv2.putText(frame,'Mouse left button Pressed',(x,y), cv2.FONT_HERSHEY_SIMPLEX,1, (255,0,0))
                cv2.imshow('video', frame)
    
        else:
            print("frame is None")
    
    def playVideoFile():
        cap = cv2.VideoCapture(r'f:videozbl1.mp4')
        fps = 1
        if not cap.isOpened():
            print("Cannot open camera")
            exit()
    
        while True:
            # 逐帧捕获
            ret, frame = cap.read()
            if not ret:
                if frame is None :
                    print("The video has end.")
                else:
                    print("Read video error!")
                break
    
            # 显示结果帧e
            cv2.imshow('video', frame)
            cv2.setMouseCallback('video', OnMouseLBUTTONDOWN, frame)
            if cv2.waitKey(int(1000/fps)) == ord('q'):  break
        # 完成所有操作后,释放捕获器
    
        cap.release()
        cv2.destroyAllWindows()
    
    playVideoFile()
    

    上面的代码读取某个视频文件逐帧显示,并在显示后设置回调函数,回调函数就是当鼠标按下时,在当前帧图片上鼠标按下位置显示’Mouse left button Pressed’。在实现上述代码时有几点需要注意:

    1. fps设置要小,否则后面的帧在屏幕上覆盖了前面加了字符的帧图像,无法看清字符;
    2. setMouseCallback 必须在每次读到新帧后设置,否则回调函数中收到的参数param就不是指向当前帧,而是调用回调函数时frame变量对应帧,有可能frame还没定义或者定义的初始值如None,这样后面的鼠标点击无法触发对当前帧的操作;
    3. setMouseCallback调用时必须在窗口已经通过cv2.namedWindow或cv2.imshow定义了窗口名字之后,否则窗口没有定义回调函数设置没有作用;
    4. 回调函数对当前帧添加了字符之后,需要再次调用cv2.imshow该帧才会刷新显示。

    更多OpenCV-Python的介绍请参考《OpenCV-Python图形图像处理》。

    关于老猿的付费专栏

    老猿的付费专栏《使用PyQt开发图形界面Python应用》专门介绍基于Python的PyQt图形界面开发基础教程,付费专栏《moviepy音视频开发专栏》详细介绍moviepy音视频剪辑合成处理的类相关方法及使用相关方法进行相关剪辑合成场景的处理,两个专栏加起来只需要19.9元,都适合有一定Python基础但无相关专利知识的小白读者学习。

    收费专栏文章目录:《moviepy音视频开发专栏文章目录》、《使用PyQt开发图形界面Python应用专栏目录》。

    对于缺乏Python基础的同仁,可以通过老猿的免费专栏《专栏:Python基础教程目录》从零开始学习Python。

    如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。

    跟老猿学Python、学OpenCV!

    ☞ ░ 前往老猿Python博文目录

  • 相关阅读:
    [转]中英文混合字符截取方法样式表解决方法
    Asp.Net小技巧集合
    [分享]整理后的Discuz!NT 2.0项目源码
    [原创]Web2.0之Tag标签原理实现浅析
    [转]CSS完美兼容IE6/IE7/FF的通用hack方法
    [转]常用CSS缩写语法总结
    文本类文件编码转换器及DiscuzNTdotNet2.0 - -
    [驳]ASP伪静态页简单教程
    SELECT中很多程序都不用的NOLOCK“加锁选项”的功能说明
    iOS 微博 OAuth2.0 分享 文字 + 图片 微博的方法
  • 原文地址:https://www.cnblogs.com/LaoYuanPython/p/13643406.html
Copyright © 2020-2023  润新知