OpenCVを使ってマウスイベント（手動）でテニスコート領域を選択できるようにする

#やりたいこと
↓の画像のようにテニスコート領域をマウスイベントで選択できるようにします。
具体的には、コート隅の４点をクリックすることで、テニスコート領域を作成します。


テニスボールの着弾点などを記録できるようにしようとしてますが、テニスコート内のどの位置に着弾したかを確認するためには、コートの座標情報が必要となります。

ちなみに、自分の過去記事ですが、[OpenCV(Python版)でテニスのボール軌道を検出する](https://qiita.com/otakoma/items/04216c60fa31eae60947)なんてこともやってます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/45007/4ac6a5a1-64f1-52aa-2f04-394c94b54593.png)

#参考にしたサイト
[その辺うろうろ27km ／ Python + OpenCV でマウスイベントを取得](http://takatakamanbou.hatenablog.com/entry/2015/04/18/235107)
こちらのサイトは、コードのかなりの部分を流用させていただきました。

[PythonとOpenCVで画像処理④【マウスイベント】](http://rasp.hateblo.jp/entry/2016/01/24/204539)
onMouse関数についてわかりやすく説明されています。

#mouse_event関数の中にマウスイベントで処理する内容を記述
１つ目は、マウスが移動したときにx方向とy方向に通しの青線を表示します。
クロスした箇所がマウスの位置となり、マウスの位置がみやすくなります。

```python
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)
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/45007/0f7b4240-d209-55be-c5df-97140c6e83c3.png)

２つ目は、マウスをクリックしたときの処理を記述します。
クリックした箇所を赤丸で囲います。
また、４点クリックしたら、４点を線でつなぎ、コート領域を線で囲うような形となります。

```python
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)
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/45007/0c11358a-e7fa-8381-fe1d-1ec41366f654.png)


#コード
```python
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()

```