<button id="ssm6u"><optgroup id="ssm6u"></optgroup></button>
  • 跳轉至

    項目實戰-自制繪圖板

    概要

    本期,阿凱從易到難, 實現了四個版本的繪圖板程序。在這節, 阿凱提高了作業的難度, 讓你做出功能豐富的繪圖板出來。

    keywords 繪圖板 Painter 鼠標事件監聽

    1. 實戰:繪圖板v1-圓形點點

    我們首先設定一個鼠標回調事件,

    我們需要指定窗口名稱windowName , 只在這個指定窗口下觸發事件.

    然后我們需要指定一下,當Mouse事件觸發時, 對應的回調函數onMouse

    # 設置鼠標事件回調
    cv2.setMouseCallback(windowName,onMouse)  
    

    使用舉例

    # 設置鼠標事件回調
    cv2.setMouseCallback('image',draw_circle)  
    

    當每次鼠標事件產生的時候, 例如鼠標移動鼠標點擊等, 就會觸發draw_circle這個函數。

    上文提及的三個onMouse 函數你都可以嘗試一下哦。 尤其是第三個。

    2.1 繪圖板v1源碼

    diy-painter-v1.py

    '''
    鼠標 每次雙擊,觸發回調函數, 在點擊處繪制一個圓圈
    
    '''
    import cv2  
    import numpy as np  
    
    # 鼠標回調函數  
    # x, y 都是相對于窗口內的圖像的位置
    
    def draw_circle(event,x,y,flags,param): 
        # 判斷事件是否為 Left Button Double Clicck 
        if event == cv2.EVENT_LBUTTONDBLCLK:  
            cv2.circle(img,(x,y),20,(255,0,0),-1)  
    
    # 創建一個黑色圖像,并綁定窗口和鼠標回調函數  
    img = np.zeros((512,512,3), np.uint8)  
    cv2.namedWindow('image')
    # 設置鼠標事件回調
    cv2.setMouseCallback('image',draw_circle)  
    
    while(True):  
        cv2.imshow('image',img)  
        if cv2.waitKey(20) == ord('q'):  
            break  
    cv2.destroyAllWindows()
    
    # 保存圖片
    cv2.imwrite("MousePaint01.png",  img)
    

    2.2 阿凱的靈魂畫作01

    20180126MousePaint01.png

    3. 實戰:繪圖板v2-線條繪制

    有了單個圓圈的繪制, 我們想一下, 如何才能繪制一條曲線呢?

    如果我移動鼠標的時候,一直繪制圓圈, 鼠標慢慢移動,這個軌跡不就是一個有寬度的曲線嘛~~

    關鍵點在于, 我們需要判斷,鼠標是否按下, 以此來判定是否需要繪制圖片。

    3.1 繪圖板v2源碼

    我們先來看一下,不用flags參數我們需要如何實現。

    我們使用isMouseLBDown 這個布爾值,記錄當前鼠標的狀態。

    diy-painter-v2.py

    '''
     鼠標按下繪制線條
    
    '''
    import cv2  
    import numpy as np  
    
    # 鼠標回調函數  
    # x, y 都是相對于窗口內的圖像的位置
    isMouseLBDown = False
    
    def draw_circle(event,x,y,flags,param): 
        # 判斷事件是否為 Left Button Double Clicck 
        print(event)
        global isMouseLBDown
    
        if event == cv2.EVENT_LBUTTONDOWN:
            # 檢測到鼠標左鍵按下
            print("mouse down")
            isMouseLBDown = True
            cv2.circle(img,(x,y),5,(255,0,0),-1) 
        elif event == cv2.EVENT_LBUTTONUP:
            # 檢測到鼠標左鍵抬起
            isMouseLBDown = False
            print("mouse up")
        elif event == cv2.EVENT_MOUSEMOVE:
            if isMouseLBDown:
                print("drawing")
                cv2.circle(img,(x,y),5,(255,0,0),-1)
    
    # 創建一個黑色圖像,并綁定窗口和鼠標回調函數  
    img = np.zeros((512,512,3), np.uint8)  
    cv2.namedWindow('image')
    # 設置鼠標事件回調
    cv2.setMouseCallback('image',draw_circle)  
    
    while(True):  
        cv2.imshow('image',img)  
        if cv2.waitKey(1) == ord('q'):  
            break  
    cv2.destroyAllWindows()
    
    cv2.imwrite("MousePaint02.png",  img)
    

    當然, 我們有了flags參數, EVENT_FLAG_LBUTTON 本身就可以判定鼠標左鍵是否按下, 所以改寫一下:

    diy-painter-v3.py

    '''
     鼠標按下繪制線條
    
    '''
    import cv2  
    import numpy as np  
    
    # 鼠標回調函數  
    # x, y 都是相對于窗口內的圖像的位置
    
    def draw_circle(event,x,y,flags,param): 
    
        if flags == cv2.EVENT_FLAG_LBUTTON:
            cv2.circle(img,(x,y),5,(255,0,0),-1)
    
    # 創建一個黑色圖像,并綁定窗口和鼠標回調函數  
    img = np.zeros((512,512,3), np.uint8)  
    cv2.namedWindow('image')
    # 設置鼠標事件回調
    cv2.setMouseCallback('image',draw_circle)  
    
    while(True):  
        cv2.imshow('image',img)  
        if cv2.waitKey(1) == ord('q'):  
            break  
    cv2.destroyAllWindows()
    
    cv2.imwrite("MousePaint03.png",  img)
    

    看, 是不是通過flags參數, 大大減少了你的代碼復雜度。

    我猜你的內心獨白可能是這樣的:“”哦,原來flags是這么用的?!?/p>

    3.2 阿凱的靈魂畫作02

    20180126MousePaint02.png

    3.3 存在問題

    1. 如果鼠標運動快了, 就會變成散點, 不連續。

    2. 只支持單個顏色, 不美麗。

    3. 不支持調節筆刷粗細。

    4. 實戰:繪圖板v3-彩色線條筆觸調節

    4.1 繪圖板v4源碼

    diy-painter-v4.py

    '''
     鼠標按下繪制線條 可以調整線條粗細 變換顏色
     線條也更流暢
    '''
    import cv2  
    import numpy as np  
    
    isMouseLBDown = False # 判斷鼠標是否摁下的標志
    circleColor = (0, 0, 0) # 畫筆的顏色
    circleRadius = 5 # 畫筆的粗細
    lastPoint = (0, 0)
    
    # 鼠標回調函數  繪制圖像 
    # x, y 都是相對于窗口內的圖像的位置
    def draw_circle(event,x,y,flags,param): 
        # 判斷事件是否為 Left Button Double Clicck 
        # print(event)
        global img
        global isMouseLBDown
        global color
        global lastPoint
    
        if event == cv2.EVENT_LBUTTONDOWN:
            # 檢測到鼠標左鍵按下
            # print("mouse down")
            isMouseLBDown = True
            cv2.circle(img,(x,y), int(circleRadius/2), circleColor,-1)
            lastPoint = (x, y)
        elif event == cv2.EVENT_LBUTTONUP:
            # 檢測到鼠標左鍵抬起
            isMouseLBDown = False
            # print("mouse up")
        elif event == cv2.EVENT_MOUSEMOVE:
            if isMouseLBDown:
                # print("drawing")
                cv2.line(img, pt1=lastPoint, pt2=(x, y), color=circleColor, thickness=circleRadius)
                lastPoint = (x, y)
                # cv2.circle(img,(x,y), circleRadius, circleColor,-1)
    
    # 更新顏色
    def updateCircleColor(x):
        global circleColor
        global colorPreviewImg
    
        r = cv2.getTrackbarPos('Channel_Red','image')
        g = cv2.getTrackbarPos('Channel_Green','image')
        b = cv2.getTrackbarPos('Channel_Blue','image')
    
        circleColor = (b, g, r)
        colorPreviewImg[:] = circleColor
    
    # 更新畫筆的寬度
    def updateCircleRadius(x):
        global circleRadius
        global radiusPreview
    
        circleRadius = cv2.getTrackbarPos('Circle_Radius', 'image')
        # 重置畫布
        radiusPreview[:] = (255, 255, 255)
        # 繪制圓形
        cv2.circle(radiusPreview, center=(50, 50), radius=int(circleRadius / 2), color=(0, 0, 0), thickness=-1)
    
    # 創建一個畫布,并綁定窗口和鼠標回調函數
    
    img = np.ones((512,512,3), np.uint8)
    img[:] = (255, 255, 255)
    
    # 用于預覽畫筆的顏色
    colorPreviewImg = np.ones((100, 100, 3), np.uint8)
    colorPreviewImg[:] = (0,  0, 0)
    # 用于預覽畫筆的粗細
    radiusPreview = np.ones((100, 100, 3), np.uint8)
    radiusPreview[:] = (255, 255, 255)
    
    # 用于展示繪圖區域的窗口
    cv2.namedWindow('image')
    # 用于預覽顏色的窗口
    cv2.namedWindow('colorPreview')
    # 用于預覽畫筆寬度的窗口
    cv2.namedWindow('radiusPreview')
    
    # 在window‘image’ 上創建一個滑動條,起名為Channel_XXX, 設定滑動范圍為0-255, 
    # onChange事件回調 啥也不做
    cv2.createTrackbar('Channel_Red','image',0,255,updateCircleColor)
    cv2.createTrackbar('Channel_Green','image',0,255,updateCircleColor)
    cv2.createTrackbar('Channel_Blue','image',0,255,updateCircleColor)
    
    cv2.createTrackbar('Circle_Radius','image',1,20,updateCircleRadius)
    # 設置鼠標事件回調
    cv2.setMouseCallback('image',draw_circle)  
    
    while(True):
        cv2.imshow('colorPreview', colorPreviewImg)
        cv2.imshow('radiusPreview', radiusPreview)
        cv2.imshow('image',img)  
        if cv2.waitKey(1) == ord('q'):  
            break
    
    cv2.destroyAllWindows()
    cv2.imwrite("MousePaint04.png",  img)
    

    4.2 阿凱的靈魂畫作04

    20180126MousePaint03.png

    繪制了自己的靈魂畫作之后, 記得發空間的時候@一下阿凱哦。

    5. 作業CH2.4

    5.1 作業1: 改寫繪圖板v1, 用滑稽臉替換圓形

    20180126huaji.jpg

    鼠標點擊過的地方都替換成滑稽臉。

    你可能需要去了解這么幾個操作:

    • 縮放圖片, 控制滑稽臉的尺寸

    • 圖片的局部替換/賦值 (需要用到numpy的索引)

    為了控制難度, 就矩形區域賦值吧,不摳滑稽臉。

    如果還是有難度, 阿凱提供給你一個我縮小過的滑稽臉吧。代碼也先提供吧, 開心比較重要

    20180126smallhuaji.png

    需要就直接下載吧, 注意修改一下后綴。

    哈哈,壞壞的阿凱, 這次不會給你代碼注釋, 自己去官網看文檔哦

    Geometric Transformations of Images - 圖像的幾何變換

    寫個博客,做做筆記也是極好的。

    import cv2
    import numpy as np
    
    def imgResize(img, ratio = 1):
        height, width = img.shape[:2]
        res = cv2.resize(img,(int(ratio*width), int(ratio*height)), interpolation = cv2.INTER_CUBIC)
        return res
    
    huaji = cv2.imread("20180126huaji.jpg")
    cv2.imwrite("smallhuaji.png",imgResize(huaji, 0.2))
    

    5.2 作業2: 給繪圖板添加繪制幾何形狀的功能

    例如,你可以給畫圖板添加矩形繪制的函數,第一次點擊的點為矩形的左上角的起點.

    接下來鼠標拖動,動態繪制矩形,鼠標彈起,確定矩形,矩形保留在畫布上.

    你也可以添加繪制圓形的功能,初始點擊的點為圓心,接下來鼠標向外拖動,半徑放大,鼠標彈起完成圓形繪制.

    5.3 作業3: 在圖片的基礎上涂鴉

    這個作業沒有解析,純開放題目

    如果圖形化有困難,可以預先設定圖片的路徑。

    通過對話框的形式,選擇一張圖片,將圖片導入到畫布,任意涂鴉,接下來保存為本地圖片.

    你可以結合其他的Python GUI組件實現此功能. 例如PyQT 例如Tkinter .

    PyQt5 file dialog

    OpenCV with Tkinter

    你甚至可以再加上一個保存按鈕, 可以選擇保存路徑。

    哈哈, 還有, 還有, 撤銷按鈕,保存最近10步操作 (適合使用數據結構:棧)

    5.4 作業4 實現SelectROI的功能

    利用作業1同樣的原理, 去選擇一個矩形區域,并且將這個矩形區域的內容保存為另外一張圖片, 并且打印矩形區域的tuple

    (x, y, w, h)
    

    矩形區域在原來圖片的左上角坐標(x,y)

    矩形區域的寬度w

    矩形區域的高度h

    6. Reference

    How to Detect Mouse Clicks and Moves


    韩国精品无码一区二区三区,精品无码一区二区三区AV,欧洲丰满美熟女乱又伦AV,亚洲午夜久久久影院伊人