Pythonに画像処理ライブラリのOpenCVを使って、簡単な線や図形、文字を描画する方法をみてきました。
この描画方法を応用して、ここではマウスを操作することで画像を描画する方法を見ていきたいと思います。
jupyter notebookではMacだとカーネルが止まってしまうことがあるのでテキストエディタでコードを書き、ターミナルから実行して別ウインドウで描画することにします。
マウスのクリックで円を描画
まず、マウスの左クリックをすると、円が描画されるコードを書いて行きましょう。
次のようなコードを書いてみました。
import cv2
import numpy as np
def draw_circle(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img,(x,y),100,(0,255,0),-1)
img = np.zeros((600,600,3), np.uint8)
cv2.namedWindow(winname='mouse_drawing')
cv2.setMouseCallback('mouse_drawing',draw_circle)
while True:
cv2.imshow('mouse_drawing',img)
if cv2.waitKey(20) & 0xFF == 27:
break
cv2.destroyAllWindows()
OpenCVとNumPyをインポート。
draw_circle()という関数を定義します。変数にマウスの操作を示すevent、マウスの座標を示すx、y、他に必要となるフラグやパラメーターをflags、paramsで与えています。
この関数は、if文でeventがマウスの左ボタンが押されている場合、つまり、cv2.EVENT_LBUTTONDOWN である時に、cv2.circle()を実行して円を描きます。cv2.circle()は、以下で与えらえる画像のimgと、円の座標と半径、色(ここでは緑)、線の太さ(ここでは-1なので塗りつぶし)が渡されています。
次にimgとして範囲となる画像を用意しています。np.zeros()を使って黒色の画像として、縦600、横600、3カラーチャンネルで作っています。np.uint8は8ビットの符号なし整数で 0から255の値のデータタイプとしてメモリを確保しています。大きなことをしないので少ないメモリを確保しているだけですから、ここは別のものでも、指定しなくても構いません。
cv.nameWindow()で、描画する画像の窓に名前をつけています。
cv2.setMouseCallback()にウインドウの名前と、上で定義した関数をコールバック関数として割り当ててマウスボタンの操作の動きと繋げます。
while文を使って、描画を実行しています。cv2.imshow()でimgの画像を表示します。if文を使って、cv2.waitKey(20) & 0xFF == 27 のとき、つまり20ミリ秒キーイベントを待ち、[esc]キーが押されたらbreakして終了させます。
cv2.destroyAllWindows()でウインドウを全て閉じます。
実際に実行して、画像をクリックして見ると次のようになります。
ESCキーを押すことで終了します。
マウスの右クリックで色の違う円を描画
今度は上のコードを少し変えて、右クリックをすれば、赤い円が描画されるコードを書いてみます。
import cv2
import numpy as np
def draw_circle(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img,(x,y),100,(0,255,0),-1)
elif event == cv2.、: # マウスの右クリックのイベント
cv2.circle(img,(x,y),100,(0,0,255),-1)
img = np.zeros((512,512,3), np.uint8)
cv2.namedWindow(winname='mouse_drawing')
cv2.setMouseCallback('mouse_drawing',draw_circle)
while True:
cv2.imshow('mouse_drawing',img)
if cv2.waitKey(20) & 0xFF == 27:
break
cv2.destroyAllWindows()
コードは先ほどのコードとほとんど同じです。
違いは、draw_circle()の定義で、elif文を追加してマウスボタンの右クリックの操作が行われた時、つまり、event == cv2.EVENT_RBUTTONDOWN の時の円を赤色で描画させている部分です。
実行して操作してみると次のようになります。
右クリックで操作した時は、赤い円が描画されました。
マウスを動かして矩形を描画
次はマウスを動かして描画するコードを書いてみます。ここでは、マウスを押して動かしている間、矩形(rectangle)を描き、マウスを離すことで描画を終わらせます。
import cv2
import numpy as np
drawing = False
ix,iy = -1,-1
def draw_rectangle(event,x,y,flags,param):
global ix,iy,drawing
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix,iy = x,y
elif event == cv2.EVENT_MOUSEMOVE:
if drawing == True:
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
# cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
img = np.zeros((600,600,3))
cv2.namedWindow(winname='my_drawing')
cv2.setMouseCallback('my_drawing',draw_rectangle)
while True:
cv2.imshow('my_drawing',img)
if cv2.waitKey(1) & 0xFF == 27:
break
cv2.destroyAllWindows()
まず、OpenCVとNumPyをインポートします。
関数を定義する前に、グローバル変数を初期化しておきます。まず、drawingをFalseとすることで、マウスが押された時にTrueとなるようにします。ix、iyはマウスが押される前の初期値の座標として定義しておきます。
関数を定義します。ここではdraw_rectangle()としてみました。渡す変数はこれまでと同じです。
この関数に、まずglobal変数を読み込みます。
if文を使って、マウスのボタンがクリックされた時の処理を定義していきます。event == cv2.EVENT_LBUTTONDOWN として、左ボタンが押された間の処理を示します。drawingをTrueにして描画をスタートさせます。左ボタンが押された時のマウスの座標をix、iyに代入して描画開始の初期座標にします。
次にelif文を使ってマウスを動かしている時の処理を定義して行きます。event == cv2.EVENT_MOUSEMOVEとして、マウスを動かしている間の処理を示します。if drawing == True: として、左ボタンが押されたままでマウスを動かしている時の処理を定義します。cv2.rectangle()で左ボタンを押したままマウスを動かして矩形を描くことになります。クリックした座標を(ix,iy)で与え、マウスの動いている座標を(x,y)で渡しています。色は緑、画像は塗り潰しを指定しています。
そして、elif文を続けてマウスのボタンを離した時の処理を定義します。 event == cv2.EVENT_LBUTTONUPとして、ボタンを離した時の処理を示します。ここでdrawing == Falseとして描画をストップさせます。(コメントアウトした部分は無くても構いません)
以上で関数の定義ができました。
imgで、np.zeros((600,600,3))を使ってこれまでと同様に黒い画像の領域を作ります。以降のコードはこれまでと一緒で、コールバック関数のところの関数名がここで定義したものになっているだけです。
描画してみます。
マウスをクリックしたまま動かした範囲で矩形が表示されています。マウスの動かしかたによってギザギザになる部分があります。
最後に
Pythonに画像処理ライブラリのOpenCVを使って、ここではマウスを操作することで画像を描画する方法をみてきました。
マウスの操作を意味するマウスイベントにはEVENT_LBUTTONDOWN、EVENT_RBUTTONDOWN、EVENT_MOUSEMOVE、EVENT_LBUTTONUPをみてきました。
マウスイベントについてはこちらも参照してください。
cv2.setMouseCallback()はマウスイベントに対して、コールバック関数を割り当てます。