【OpenCVを使ったスポーツ画像解析4(モーショントラッキング)】テニスコートのラインの自動検出
公開日:
最終更新日:2018/07/27
2018/07/27
モーショントラッキングの続きです。
先回【OpenCVを使ったスポーツ画像解析3(モーショントラッキング)】選手やテニスボールの移動を自動で検出して移動軌跡をトラッキングでテニスボールや選手の移動をトラッキングをできるようにしました。
次は、テニスコートのラインを自動検出できるようにしたいと思います。
元画像は↓です。
(2018/4/26)申し訳ありません。画像は削除しました。
まず、画像を2値化します。
import cv2 import numpy as np img=cv2.imread("img.jpg",1)#画像を読み込む gray=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#グレー画像に変換 retval, black = cv2.threshold(gray, 122, 255, cv2.THRESH_BINARY)#画像を2値化
(2018/4/26)申し訳ありません。画像は削除しました。
2値化した画像の白い領域を輪郭検出すると↓のような画像となります。
テニスコート以外の領域もたくさん輪郭検出されます。
image,contours, hierarchy = cv2.findContours(black,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE) #輪郭の中で面積が最大となる輪郭を検出 for c in contours: epsilon = 0.001 * cv2.arcLength(c, True) approx = cv2.approxPolyDP(c, epsilon, True) cv2.drawContours(img, [approx], -1, (0, 255, 0), 2)
(2018/4/26)申し訳ありません。画像は削除しました。
テニスコートだけを検出するために、輪郭図形の中で面積が最大のものだけ抽出して表示するようにします。
中央のネットが邪魔をして綺麗な台形を検出できず、いびつな形となっています。
ですが、4隅の点座標がわかればいいので、この図形でこのまま解析を続けます。
#輪郭の中で面積が最大となる輪郭を検出 max_area=0 for c in contours: area = cv2.contourArea(c) if(area>max_area): max_area=area temp=c #近似 epsilon = 0.001 * cv2.arcLength(temp, True) approx = cv2.approxPolyDP(temp, epsilon, True) cv2.drawContours(img, [approx], -1, (0, 255, 0), 2)
(2018/4/26)申し訳ありません。画像は削除しました。
approxには全点の座標データが格納されており、そこから、下記やり方で4隅の点を検出します。
・左下 x座標が一番小さい点を抽出
・右下 x座標が一番大きい点を抽出
・左上 一番左上に近い点を抽出するために、135°回転させた座標軸で一番大きい点を抽出
・右上 一番右上に近い点を抽出するために、45°回転させた座標軸で一番大きい点を抽出
4点をつなぐと次のような画像となります。
import numpy as np #回転マトリクスを作成 def createRotateMat(degree): import math rad=math.radians(degree) return np.array([[math.cos(rad),-math.sin(rad)],[math.sin(rad),math.cos(rad)]]) rotate1=createRotateMat(135)#左上検出用の回転マトリクス rotate2=createRotateMat(45)#右上検出用の回転マトリクス #初期座標 min_x_point=approx[0][0] max_x_point=approx[0][0] left_max_point=approx[0][0] right_max_point=approx[0][0] left_max_point_temp=np.array(approx[0][0]) left_max_point_rot=rotate1.dot(left_max_point_temp) right_max_point_temp=np.array(approx[0][0]) right_max_point_rot=rotate2.dot(right_max_point_temp) #近似図形の全部の点を参照して4隅の点を検出 for i in range(len(approx)): x=approx[i][0][0] y=approx[i][0][1] if(min_x_point[0]>x):#左下検出 xが一番小さい min_x_point=[x,y] if(max_x_point[0]<x):#右下検出 xが一番大きい max_x_point=[x,y] point=np.array([x,y]) rotate_point1=rotate1.dot(point) rotate_point2=rotate2.dot(point) if(left_max_point_rot[0]<rotate_point1[0]):#左上検出 回転後のxが一番大きい left_max_point_rot=rotate_point1 left_max_point=[x,y] if(right_max_point_rot[0]<rotate_point2[0]):#右上検出 回転後のxが一番小さい right_max_point_rot=rotate_point2 right_max_point=[x,y] point3=np.array(min_x_point) point4=np.array(max_x_point) point1=np.array(left_max_point) point2=np.array(right_max_point) pts=np.array([point1,point2,point4,point3],dtype=int) img=cv2.imread("img1.jpg",1) cv2.polylines(img,[pts],True,(0,255,0),2) cv2.imshow("img",img) cv2.waitKey(0)
(2018/4/26)申し訳ありません。画像は削除しました。
検出した線はダブルスコートなので、シングルスコートに変換します。
ダブルスコートの線長は10.97m
シングルスコートの線長は8.23m
なので、その比率をとって、シングルスコートの4隅の点を算出します。
4点をつなぐと次のような画像となります。
len1=np.linalg.norm(point2-point1)#左下点と右下点の距離 len2=np.linalg.norm(point4-point3)#左上点と右上点の距離 #シングルスコートの検出 point1_1=point1+1/len1*(point2-point1)*(10.97-8.23)/2/10.97*len1 point2_1=point2-1/len1*(point2-point1)*(10.97-8.23)/2/10.97*len1 point3_1=point3+1/len2*(point4-point3)*(10.97-8.23)/2/10.97*len2 point4_1=point4-1/len2*(point4-point3)*(10.97-8.23)/2/10.97*len2 pts=np.array([point1_1,point2_1,point4_1,point3_1],dtype=int) img=cv2.imread("img1.jpg",1) cv2.polylines(img,[pts],True,(0,255,0),2) cv2.imshow("img",img) cv2.waitKey(0)
(2018/4/26)申し訳ありません。画像は削除しました。
実際の線に対して少しずれがありますが、まあまあの精度でシングルスコートのラインを検出することができました。
スポーツ画像解析リンク
・【OpenCVを使ったスポーツ画像解析1】テニス選手の移動量や軌跡をデータ化する
・【OpenCVを使ったスポーツ画像解析2】フェデラー選手の移動軌跡をグラフ化してみました
・【OpenCVを使ったスポーツ画像解析3】選手やテニスボールの移動を自動で検出して移動軌跡をトラッキング
・【OpenCVを使ったスポーツ画像解析4】テニスコートのラインの自動検出
・【OpenCVを使ったスポーツ画像解析5】画像からテニス選手の位置を検出する
・【OpenCVを使ったスポーツ画像解析6】深層学習(ディープラーニング)を用いてテニス選手とボールをトラッキング