前提・実現したいこと
テニスの動画を差分処理のみを用いて、ボールのトラッキングを行うプログラムを書いています。
プログラムを実行してみたところ動きはしたのですが、ノイズが多かったことと
差分処理のため選手の動きも出てしまうことが問題で改善したいと考えています。
そこで、
1.テニスコート内のものだけ差分処理を行う
2.テニスボールの大きさ以下のものだけ差分処理を行う
という2点の改善をしたいです。
該当のソースコード1
Python
1import time 2import math 3import cv2 4import sympy 5import numpy as np 6 7def isCourt(p):#ボール座標がコートの中にあるかを判定 8 from sympy.geometry import Point, Polygon 9 poly = Polygon((520,178), (905,178), (1065,600), (360,600)) 10 point=p 11 return poly.encloses_point(point) 12 13def dilation(dilationSize,kernelSize,img):#膨張処理 14 kernel=np.ones((kernelSize,kernelSize),np.uint8) 15 element=cv2.getStructuringElement(cv2.MORPH_RECT,(2*dilationSize+1,2*dilationSize+1),(dilationSize,dilationSize)) 16 dilation_img = cv2.dilate(img,kernel,element) 17 18 return dilation_img 19 20def detect(gray_diff):# 21 retval, black_diff = cv2.threshold(gray_diff, 30, 255, cv2.THRESH_BINARY) 22 img=black_diff 23 24 dilation_img=dilation(18,20,img) 25 26 img2=dilation_img.copy() 27 img2=cv2.cvtColor(img2,cv2.COLOR_GRAY2RGB) 28 29 contours, hierarchy = cv2.findContours(dilation_img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) 30 31 temp=[] 32 p_array=[] 33 area_array=[] 34 ball_p=[] 35 for i in range(len(contours)): 36 37 count=len(contours[i]) 38 temp.append(count) 39 area = cv2.contourArea(contours[i])#面積計算 40 area_array.append(area) 41 x=0.0 42 y=0.0 43 for j in range(count): 44 x+=contours[i][j][0][0] 45 y+=contours[i][j][0][1] 46 47 x/=count 48 y/=count 49 x=int(x) 50 y=int(y) 51 p_array.append([x,y]) 52 53 if(area<1000):#面積が一定以下 54 if(isCourt([x,y])):#コート内かどうか 55 cv2.drawContours(img2,contours,i,(0,255,255),2)#黄 56 ball_p=[x,y] 57 return img2,p_array,area_array,ball_p 58 59VIDEO_DATA = "Movie.mp4" 60ESC_KEY = 0x1b 61DURATION = 1.0 62cv2.namedWindow("tennis") 63video = cv2.VideoCapture(VIDEO_DATA) 64 65fps = video.get(cv2.CAP_PROP_FPS) 66height = video.get(cv2.CAP_PROP_FRAME_HEIGHT) 67width = video.get(cv2.CAP_PROP_FRAME_WIDTH) 68# 形式はMP4Vを指定 69fourcc = cv2.VideoWriter_fourcc(*'MJPG') 70# 出力先のファイルを開く 71out = cv2.VideoWriter('output.avi',fourcc,20.0, (int(width), int(height))) 72 73# 最初のフレームの読み込み 74end_flag, frame_next = video.read()#read() 1つ1つのフレームを読み込む 75height, width, channels = frame_next.shape 76motion_history = np.zeros((height, width), np.float32) 77frame_pre = frame_next.copy() 78 79p=[] 80a=[] 81ball=[] 82 83 84while(end_flag): 85#for i in range(150): 86 color_diff = cv2.absdiff(frame_next, frame_pre)# フレーム間の差分計算 87 gray_diff = cv2.cvtColor(color_diff, cv2.COLOR_BGR2GRAY)# グレースケール変換 88 retval, black_diff = cv2.threshold(gray_diff, 100, 1, cv2.THRESH_BINARY)# 2値化 89 90 proc_time = time.time()# プロセッサ処理時間(sec)を取得 91 cv2.motempl.updateMotionHistory(black_diff, motion_history, proc_time, DURATION)# モーション履歴画像の更新 92 hist_color = np.array(np.clip((motion_history - (proc_time - DURATION)) / DURATION, 0, 1) * 255, np.uint8)# 古いモーションの表示を経過時間に応じて薄くする 93 hist_gray = cv2.cvtColor(hist_color, cv2.COLOR_GRAY2BGR)# グレースケール変換 94 img,p_temp,a_temp,ball_temp=detect(gray_diff) 95 96 #if(i>50): 97 p.append(p_temp) 98 a.append(a_temp) 99 if(len(ball_temp)>0): 100 ball.append(ball_temp) 101 102 ball_array=np.array(ball) 103 court_array=np.array([[520,178], [905,178], [1065,600], [360,600]]) 104 service1_array=np.array([[440,250],[1000,250]]) 105 service2_array=np.array([[380,420],[1050,420]]) 106 service3_array=np.array([[712,250],[712,420]]) 107 img=cv2.polylines(img,[ball_array],False,(0,255,255)) 108 img=cv2.polylines(img,[court_array],True,(0,255,0)) 109 img=cv2.polylines(img,[service1_array],True,(0,255,0)) 110 img=cv2.polylines(img,[service2_array],True,(0,255,0)) 111 img=cv2.polylines(img,[service3_array],True,(0,255,0)) 112 113 cv2.imshow("tennis", img)# モーション画像を表示 114 out.write(img) 115 116 if cv2.waitKey(20) == ESC_KEY:# Escキー押下で終了 117 break 118 119 # 次のフレームの読み込み 120 frame_pre = frame_next.copy() 121 end_flag, frame_next = video.read() 122 123# 終了処理 124cv2.destroyAllWindows() 125out.release() 126video.release() 127
該当のソースコード2
Python
1 2import time 3import math 4import cv2 5import sympy 6import numpy as np 7 8def isCourt(p):#ボール座標がコートの中にあるかを判定 9 from sympy.geometry import Point, Polygon 10 poly = Polygon((520,178), (895,178), (1065,600), (360,600)) 11 point=p 12 return poly.encloses_point(point) 13 14def dilation(dilationSize,kernelSize,img):#膨張処理 15 kernel=np.ones((kernelSize,kernelSize),np.uint8) 16 element=cv2.getStructuringElement(cv2.MORPH_RECT,(2*dilationSize+1,2*dilationSize+1),(dilationSize,dilationSize)) 17 dilation_img = cv2.dilate(img,kernel,element) 18 19 return dilation_img 20 21def detect(gray_diff):# 22 retval, black_diff = cv2.threshold(gray_diff, 30, 255, cv2.THRESH_BINARY) 23 img=black_diff 24 25 dilation_img=dilation(18,20,img) 26 27 img2=dilation_img.copy() 28 img2=cv2.cvtColor(img2,cv2.COLOR_GRAY2RGB) 29 30 contours, hierarchy = cv2.findContours(dilation_img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE,offset=(xmin,ymin)) 31 32 33 34 35 36 37 temp=[] 38 p_array=[] 39 area_array=[] 40 ball_p=[] 41 for i in range(len(contours)): 42 43 count=len(contours[i]) 44 temp.append(count) 45 area = cv2.contourArea(contours[i])#面積計算 46 area_array.append(area) 47 x=0.0 48 y=0.0 49 for j in range(count): 50 x+=contours[i][j][0][0] 51 y+=contours[i][j][0][1] 52 53 x/=count 54 y/=count 55 x=int(x) 56 y=int(y) 57 p_array.append([x,y]) 58 59 if(area<1000):#面積が一定以下 60 if(isCourt([x,y])):#コート内かどうか 61 cv2.drawContours(img2,contours,i,(0,255,255),2)#黄 62 ball_p=[x,y] 63 return img2,p_array,area_array,ball_p 64 65xmin,xmax=360,1065 66ymin,ymax=178,600 67 68VIDEO_DATA = "Nishikori (錦織) VS Tsonga (ツォンガ).mp4" 69ESC_KEY = 0x1b 70DURATION = 1.0 71cv2.namedWindow("motion") 72video = cv2.VideoCapture(VIDEO_DATA) 73 74fps = video.get(cv2.CAP_PROP_FPS) 75height = video.get(cv2.CAP_PROP_FRAME_HEIGHT) 76width = video.get(cv2.CAP_PROP_FRAME_WIDTH) 77fourcc = cv2.VideoWriter_fourcc(*'MJPG') 78 79out = cv2.VideoWriter('output.avi',fourcc,20.0, (int(width), int(height))) 80 81 82end_flag, frame_next = video.read()#read() 1つ1つのフレームを読み込む 83detframe=frame_next[ymin:ymax,xmin:xmax] 84height, width, channels = detframe.shape 85motion_history = np.zeros((height, width), np.float32) 86frame_pre = detframe.copy() 87 88p=[] 89a=[] 90ball=[] 91 92 93while(end_flag): 94 color_diff = cv2.absdiff(detframe, frame_pre)# フレーム間の差分計算 95 gray_diff = cv2.cvtColor(color_diff, cv2.COLOR_BGR2GRAY)# グレースケール変換 96 retval, black_diff = cv2.threshold(gray_diff, 100, 1, cv2.THRESH_BINARY)# 97 98 proc_time = time.time()# プロセッサ処理時間(sec)を取得 99 cv2.motempl.updateMotionHistory(black_diff, motion_history, proc_time, DURATION)# モーション履歴画像の更新 100 hist_color = np.array(np.clip((motion_history - (proc_time - DURATION)) / DURATION, 0, 1) * 255, np.uint8)# 古いモーションの表示を経過時間に応じて薄くする 101 hist_gray = cv2.cvtColor(hist_color, cv2.COLOR_GRAY2BGR)# グレースケール変換 102 img,p_temp,a_temp,ball_temp=detect(gray_diff) 103 104 105 p.append(p_temp) 106 a.append(a_temp) 107 if(len(ball_temp)>0): 108 ball.append(ball_temp) 109 110 ball_array=np.array(ball) 111 court_array=np.array([[520,178], [895,178], [1065,600], [360,600]]) 112 img = cv2.polylines(img,[ball_array],False,(0,255,255)) 113 img=cv2.polylines(img,[court_array],True,(0,255,0)) 114 115 cv2.imshow("motion", img)# モーション画像を表示 116 out.write(img) 117 118 if cv2.waitKey(20) == ESC_KEY:# Escキー押下で終了 119 break 120 121 122 frame_pre = detframe.copy() 123 end_flag, frame_next = video.read() 124 125 126cv2.destroyAllWindows() 127out.release() 128video.release() 129
試したこと
ソースコード1では下記のような映像が表示されました。
ボールの軌道をトラッキングしたかったのですがノイズが多いためコート内のものだけを
差分処理しようと考え、ソースコード2のように修正しました。
ソースコード2では下記のような画像が表示されました。
理想としてはコート内の差分処理をきれいに行った後、ボールのサイズ以下の
もののみの差分処理を行うことで、きれいにボールのトラッキングをしたいです。
お力添えの方よろしくお願い致します。