EnsekiTT Blog

EnsekiTTが書くブログです。

Python+OpenCVでMouseイベントを取得してお絵かきする話

つまりなにしたの?

Python+OpenCVでMouseイベントを取得してお絵かきをした
f:id:ensekitt:20180703000017j:plain

なんでやろうとおもったの?

矩形をクリックして取得する等はすでにあるライブラリでできたけど、
クリック位置が欲しいタイミングがあったので先にMouseイベントを取得できるようにした。
ただ、UIとWebカメラの組み合わせのベストプラクティスかはわからないのでなんとも言えない。

やったこと

Webカメラから画像を取り込む。
毎フレーム画面にマウスでクリックしている間に動いた範囲に青い円を書く。
Mouse as a Paint-Brush — OpenCV 3.0.0-dev documentation
whitecat-student.hatenablog.com
こちらを参考にさせていただいてやってみた。

コード

import cv2

class mouseParam:
    def __init__(self, input_img_name):
        #マウス入力用のパラメータ
        self.mouseEvent = {"x":None, "y":None, "event":None, "flags":None}
        #マウス入力の設定
        cv2.setMouseCallback(input_img_name, self.__CallBackFunc, None)

    #コールバック関数
    def __CallBackFunc(self, eventType, x, y, flags, userdata):

        self.mouseEvent["x"] = x
        self.mouseEvent["y"] = y
        self.mouseEvent["event"] = eventType
        self.mouseEvent["flags"] = flags

    #マウス入力用のパラメータを返すための関数
    def getData(self):
        return self.mouseEvent

    #マウスイベントを返す関数
    def getEvent(self):
        return self.mouseEvent["event"]

    #マウスフラグを返す関数
    def getFlags(self):
        return self.mouseEvent["flags"]

    #xとyの座標を返す関数
    def getPos(self):
        return (self.mouseEvent["x"], self.mouseEvent["y"])

cap = cv2.VideoCapture(0)
click_points = []
draw = False
#コールバックの設定
window_name = "main window"
while True:
    # VideoCaptureから1フレーム読み込む
    ret, frame = cap.read()
    # スクリーンショットを撮りたい関係で1/4サイズに縮小
    frame = cv2.resize(frame, (int(frame.shape[1]/2), int(frame.shape[0]/2)))

    # 描画する
    [cv2.circle(frame, point, 20, (255,0,0), thickness=1, lineType=cv2.LINE_8, shift=0) for point in click_points]
    cv2.imshow(window_name, frame)

    # 描画結果の中でマウスの状態を取得する
    mouseData = mouseParam(window_name)

    # キー入力を1ms待って、k が27(ESC)だったらBreakする
    k = cv2.waitKey(1)
    if k == 27:
        break

    #左クリックがあったら表示
    if mouseData.getEvent() == cv2.EVENT_LBUTTONDOWN: # 左ボタンを押下したとき
        draw = True
        click_points.append(mouseData.getPos())
    if mouseData.getEvent() == cv2.EVENT_LBUTTONUP: # 左ボタンを上げたとき
        draw = False
    if mouseData.getEvent() == cv2.EVENT_MOUSEMOVE and draw: # マウスが動いた時
        print(mouseData.getPos())
        if draw:
            click_points.append(mouseData.getPos())

cap.release()
cv2.destroyAllWindows()

できました

f:id:ensekitt:20180702234705p:plain

やってみてどうだった?

どう考えてもシーケンシャルに入ってくる動画に対してはベストプラクティスじゃないことまではわかった。
非同期的にやるべきだし、静止画を前提とされているようにも感じたし、その用途では使いやすそう。

クリエイティブ・コモンズ・ライセンス
この 作品 は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。