Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

やりたいこと

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

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

ちなみに、自分の過去記事ですが、OpenCV(Python版)でテニスのボール軌道を検出するなんてこともやってます。

image.png

参考にしたサイト

その辺うろうろ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)

image.png

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)

image.png

コード

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()

otakoma
テニスとプログラミングが好きで、スコア記録アプリ(Androidアプリ)やデータ分析サイトをつくってます。他にはディープラーニングや画像解析を用いたテニスの試合やフォームの解析など。製造業で働いておりROSにも興味あり。本職はメカ設計エンジニアですが、ソフトウエアエンジニアになりたい。 https://github.com/taikoma
http://datatennis.net/about/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away