やりたいこと
↓の画像のようにテニスコート領域をマウスイベントで選択できるようにします。
具体的には、コート隅の4点をクリックすることで、テニスコート領域を作成します。
テニスボールの着弾点などを記録できるようにしようとしてますが、テニスコート内のどの位置に着弾したかを確認するためには、コートの座標情報が必要となります。
ちなみに、自分の過去記事ですが、OpenCV(Python版)でテニスのボール軌道を検出するなんてこともやってます。
参考にしたサイト
その辺うろうろ27km / Python + OpenCV でマウスイベントを取得
こちらのサイトは、コードのかなりの部分を流用させていただきました。
PythonとOpenCVで画像処理④【マウスイベント】
onMouse関数についてわかりやすく説明されています。
mouse_event関数の中にマウスイベントで処理する内容を記述
1つ目は、マウスが移動したときにx方向とy方向に通しの青線を表示します。
クロスした箇所がマウスの位置となり、マウスの位置がみやすくなります。
if event == cv2.EVENT_MOUSEMOVE: # マウスが移動したときにx線とy線を更新する
img2 = np.copy(img)
h, w = img2.shape[0], img2.shape[1]
cv2.line(img2, (x, 0), (x, h - 1), (255, 0, 0))
cv2.line(img2, (0, y), (w - 1, y), (255, 0, 0))
cv2.imshow(wname, img2)
2つ目は、マウスをクリックしたときの処理を記述します。
クリックした箇所を赤丸で囲います。
また、4点クリックしたら、4点を線でつなぎ、コート領域を線で囲うような形となります。
if event == cv2.EVENT_LBUTTONDOWN: # レフトボタンをクリックしたとき、ptlist配列にx,y座標を格納する
if ptlist.add(x, y):
print('[%d] ( %d, %d )' % (ptlist.pos - 1, x, y))
cv2.circle(img, (x, y), 3, (0, 0, 255), 3)
cv2.imshow(wname, img)
else:
print('All points have selected. Press ESC-key.')
if(ptlist.pos == ptlist.npoints):
print(ptlist.ptlist)
cv2.line(img, (ptlist.ptlist[0][0], ptlist.ptlist[0][1]),
(ptlist.ptlist[1][0], ptlist.ptlist[1][1]), (0, 255, 0), 3)
cv2.line(img, (ptlist.ptlist[1][0], ptlist.ptlist[1][1]),
(ptlist.ptlist[2][0], ptlist.ptlist[2][1]), (0, 255, 0), 3)
cv2.line(img, (ptlist.ptlist[2][0], ptlist.ptlist[2][1]),
(ptlist.ptlist[3][0], ptlist.ptlist[3][1]), (0, 255, 0), 3)
cv2.line(img, (ptlist.ptlist[3][0], ptlist.ptlist[3][1]),
(ptlist.ptlist[0][0], ptlist.ptlist[0][1]), (0, 255, 0), 3)
コード
import numpy as np
import cv2
class PointList():
def __init__(self, npoints):
self.npoints = npoints
self.ptlist = np.empty((npoints, 2), dtype=int)
self.pos = 0
def add(self, x, y):
if self.pos < self.npoints:
self.ptlist[self.pos, :] = [x, y]
self.pos += 1
return True
return False
def onMouse(event, x, y, flag, params):
wname, img, ptlist = params
if event == cv2.EVENT_MOUSEMOVE: # マウスが移動したときにx線とy線を更新する
img2 = np.copy(img)
h, w = img2.shape[0], img2.shape[1]
cv2.line(img2, (x, 0), (x, h - 1), (255, 0, 0))
cv2.line(img2, (0, y), (w - 1, y), (255, 0, 0))
cv2.imshow(wname, img2)
if event == cv2.EVENT_LBUTTONDOWN: # レフトボタンをクリックしたとき、ptlist配列にx,y座標を格納する
if ptlist.add(x, y):
print('[%d] ( %d, %d )' % (ptlist.pos - 1, x, y))
cv2.circle(img, (x, y), 3, (0, 0, 255), 3)
cv2.imshow(wname, img)
else:
print('All points have selected. Press ESC-key.')
if(ptlist.pos == ptlist.npoints):
print(ptlist.ptlist)
cv2.line(img, (ptlist.ptlist[0][0], ptlist.ptlist[0][1]),
(ptlist.ptlist[1][0], ptlist.ptlist[1][1]), (0, 255, 0), 3)
cv2.line(img, (ptlist.ptlist[1][0], ptlist.ptlist[1][1]),
(ptlist.ptlist[2][0], ptlist.ptlist[2][1]), (0, 255, 0), 3)
cv2.line(img, (ptlist.ptlist[2][0], ptlist.ptlist[2][1]),
(ptlist.ptlist[3][0], ptlist.ptlist[3][1]), (0, 255, 0), 3)
cv2.line(img, (ptlist.ptlist[3][0], ptlist.ptlist[3][1]),
(ptlist.ptlist[0][0], ptlist.ptlist[0][1]), (0, 255, 0), 3)
if __name__ == '__main__':
img = cv2.imread("tennis.jpg")
wname = "MouseEvent"
cv2.namedWindow(wname)
npoints = 4
ptlist = PointList(npoints)
cv2.setMouseCallback(wname, onMouse, [wname, img, ptlist])
cv2.imshow(wname, img)
cv2.waitKey()
cv2.destroyAllWindows()