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:\video\zbl1.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博文目錄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM