Macでマウスを使ってimage上に矩形領域を描画する(2021.7.7)

Summary

Macでマウスを使って矩形領域を指定して画面キャプチャする(2021.7.7)のはできるようになったので、今回は静止画像を表示し、その上にマウスを使って矩形領域を指定して、その領域だけをキャプチャする。

cv2.setMouseCallback()でマウスイベントを取得して描画

マウスイベントの取得

マウスイベントの取得の方法は、基本的にMacでマウスを使って矩形領域を指定して画面キャプチャする(2021.7.7)と同じで、call back関数を定義し、その関数でevent, x, y, flags, paramを引数として受け取るようにする。

cv2.namedWindow('Type [q] to quit capturing')
cv2.setMouseCallback('Type [q] to quit capturing', draw_circle)

cv2.namedWindow()で設定した名前の文字列と同一文字列で対象のwindowを特定できるようにしてcall back関数を指定する。
この対応が誤っていると、

 (-27:Null pointer) NULL window handler in function 'cvSetMouseCallback'.

などとおこられます。

### 表示した画像にマウス操作でインポーズ描画する

whileループ中で、毎回元画像からコピーして再描画することで軌跡を消去する。
draw_circleの中では、クリックイベントが発生するごとにポジションをpt0に保存する。
一度マウスがクリックされたなら、画面内のどこか(最新のpt0の場所)に赤丸を書き続ける。

def draw_circle(event, x, y, flags, param):
    global circle_clicked, pt0
    print(x, y, event, flags, param, cv2.EVENT_LBUTTONDOWN, circle_clicked)
    if event == cv2.EVENT_LBUTTONDOWN:
        print("Draw!!! (%d, %d), %s" % (x, y, circle_clicked))
        # pt0 shall be updeted for every Left click
        pt0 = (x, y)
        if circle_clicked == False:
            circle_clicked = True
           
while True:
    # Clear image for drowing new cirle and rectancle
    frame = image.copy()

    if topLeft_clicked == True and botRight_clicked == False:
        cv2.rectangle(frame, pt1, pt2, (0, 255, 0), 1)
        cv2.circle(frame, center=pt1, radius=5, color=(0,0,255), thickness=-1)
    elif topLeft_clicked == True and botRight_clicked == True:
        cv2.rectangle(frame, pt1, pt2, (0, 0, 255), 2)

    # show the frame
    cv2.imshow('Type [q] to quit capturing', frame)

マウスクリックで矩形領域を指定する

マウスで左上と右下の座標を取得し長方形をインポーズ描画する

カメラ画像の取得したい矩形領域の左上と右下の2点でマウス左クリックすると、領域を選択できる。その状態でsキーを押すと領域を切り出して画像として保存する。
選択された領域と切り出された画像が一致していないのは、キーを押す操作をしているあいだに手持ちの被写体がずれているからです。

camera image

cropped image

1回目のクリックが完了すると、現在選択されている区画を緑線で表示する。
2度目にクリックすると赤枠になり選択領域が確定する。

Macでマウスで指定した矩形領域をキャプチャするPythonスクリプト

完成したスクリプトは次のようになります。 領域の選択が完了した状態でsキーを押すと、赤枠で囲まれた領域を切り出して./photo_crop.jpgに保存し、赤枠で領域を確定していない城代でsキーが押された場合は、全体をキャプチャーして./photo.jpgに保存します。qで終了。

ソースコードはこちら(test_image_impose_test.py


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# =======================================================
#  opencvを使ってマウスで矩形領域を指定して静止画上にインポーズする
#
#  test_image_impose.py
#  coded by Noboru Harada (noboru@ieee.org)
#
#  Changes:
#  2021/07/07: First version
# =======================================================

import cv2

def draw_circle(event, x, y, flags, param):
    global circle_clicked, pt0
    print(x, y, event, flags, param, cv2.EVENT_LBUTTONDOWN, circle_clicked)
    if event == cv2.EVENT_LBUTTONDOWN:
        print("Draw!!! (%d, %d), %s" % (x, y, circle_clicked))
        # pt0 shall be updeted for every Left click
        pt0 = (x, y)
        if circle_clicked == False:
            circle_clicked = True

def draw_rectangle(event, x, y, flags, param):
    global pt1, pt2, topLeft_clicked, botRight_clicked
    print(x, y, event, flags, param, cv2.EVENT_LBUTTONDOWN, topLeft_clicked, botRight_clicked)
    if event == cv2.EVENT_LBUTTONDOWN:
        if topLeft_clicked == True and botRight_clicked == True:
            # release botRight_clicked
            botRight_clicked = False
            # renew topLeft_clicked
            topLeft_clicked = True
            pt1 = (x, y)
            pt2 = (x, y)
        elif topLeft_clicked == False and botRight_clicked == False:
            pt1 = (x, y)
            topLeft_clicked = True
        elif topLeft_clicked == True and botRight_clicked == False:
            pt2 = (x, y)
            botRight_clicked = True
        print("Draw!!! (%d, %d), %s, %s" % (x, y, topLeft_clicked, botRight_clicked))
    elif botRight_clicked == False:
        pt2 = (x, y)

# init position values
pt0 = (0, 0)
pt1 = (0, 0)
pt2 = (0, 0)
circle_clicked = False
topLeft_clicked = False
botRight_clicked = False

print("Type 'q' to quit capturing")

image = cv2.imread('./infile.jpg')
cv2.imshow('Type [q] to quit capturing', image)
cv2.namedWindow('Type [q] to quit capturing')

cv2.setMouseCallback('Type [q] to quit capturing', draw_rectangle)

while True:
    # Clear image for drowing new cirle and rectancle
    frame = image.copy()

    if topLeft_clicked == True and botRight_clicked == False:
        cv2.rectangle(frame, pt1, pt2, (0, 255, 0), 1)
        cv2.circle(frame, center=pt1, radius=5, color=(0,0,255), thickness=-1)
    elif topLeft_clicked == True and botRight_clicked == True:
        cv2.rectangle(frame, pt1, pt2, (0, 0, 255), 2)

    # show the frame
    cv2.imshow('Type [q] to quit capturing', frame)

    key = cv2.waitKey(1) & 0xFF
    
    if key == ord('q'):
        break
    if key == ord('s'):
        frame = image
        if topLeft_clicked == True and botRight_clicked == True:
            x1, y1 = pt1
            x2, y2 = pt2
            if x1 > x2 or y1 > y2:
                x1, y1 = pt2
                x2, y2 = pt1
            crop_frame = image[y1 : y2, x1 : x2]
            filename = "./photo_crop.jpg"
            cv2.imwrite(filename,crop_frame)
        else:
            filename = "./photo.jpg"
            cv2.imwrite(filename,image)

# terminate resources
cv2.destroyAllWindows()

参考

OpenCVでのマウスイベント取得とカメラ画像へのインポーズ描画

Back to Index