質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

1151閲覧

範囲指定と一定サイズ以下の差分処理の書き方

mituo

総合スコア0

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/12/09 07:35

編集2021/12/10 03:09

前提・実現したいこと

テニスの動画を差分処理のみを用いて、ボールのトラッキングを行うプログラムを書いています。
プログラムを実行してみたところ動きはしたのですが、ノイズが多かったことと
差分処理のため選手の動きも出てしまうことが問題で改善したいと考えています。
そこで、
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では下記のような画像が表示されました。
イメージ説明
理想としてはコート内の差分処理をきれいに行った後、ボールのサイズ以下の
もののみの差分処理を行うことで、きれいにボールのトラッキングをしたいです。
お力添えの方よろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tiitoi

2021/12/09 08:43

detframe=frame_next[ymin:ymax,xmin:xmax] がコードに見当たらないのですが、どこのことを指していますか?
mituo

2021/12/10 00:12

言葉が足らず申し訳ございません。 上のプログラムを""試したこと""のように書き換えた結果がうまく実行できなかったので、改善した ソースプログラムではなく改善前のソースプログラムを提示しました。 改善後のプログラムは74行目以降、下記の通りです。 end_flag, frame_next = video.read()#read() 1つ1つのフレームを読み込む detframe=frame_next[ymin:ymax,xmin:xmax] height, width, channels = detframe.shape motion_history = np.zeros((height, width), np.float32) frame_pre = detframe.copy() p=[] a=[] ball=[] while(end_flag): #for i in range(150): color_diff = cv2.absdiff(detframe, frame_pre)# フレーム間の差分計算 gray_diff = cv2.cvtColor(color_diff, cv2.COLOR_BGR2GRAY)# グレースケール変換 retval, black_diff = cv2.threshold(gray_diff, 100, 1, cv2.THRESH_BINARY)# 2値化 proc_time = time.time()# プロセッサ処理時間(sec)を取得 cv2.motempl.updateMotionHistory(black_diff, motion_history, proc_time, DURATION)# モーション履歴画像の更新 hist_color = np.array(np.clip((motion_history - (proc_time - DURATION)) / DURATION, 0, 1) * 255, np.uint8)# 古いモーションの表示を経過時間に応じて薄くする hist_gray = cv2.cvtColor(hist_color, cv2.COLOR_GRAY2BGR)# グレースケール変換 img,p_temp,a_temp,ball_temp=detect(gray_diff) #if(i>50): p.append(p_temp) a.append(a_temp) if(len(ball_temp)>0): ball.append(ball_temp) ball_array=np.array(ball) court_array=np.array([[520,178], [895,178], [1065,600], [360,600]]) img = cv2.polylines(img,[ball_array],False,(0,255,255)) img=cv2.polylines(img,[court_array],True,(0,255,0)) cv2.imshow("motion", img)# モーション画像を表示 out.write(img) if cv2.waitKey(20) == ESC_KEY:# Escキー押下で終了 break # 次のフレームの読み込み frame_pre = detframe.copy() end_flag, frame_next = video.read() # 終了処理 cv2.destroyAllWindows() out.release() video.release()
fana

2021/12/10 01:17

> しかし、実行結果は画面が小さくなっただけでした。 とはどういう意味か?(何が問題なのか?) あるサイズの映像のうちの一部のみを処理対象としたのであれば,その処理対象の大きさというのは当然ながら元の映像のサイズ以下となるであろうが,それが不満だという話か? (処理対象を変えたならば isCourt にハードコーディングされている座標値みたなのも修正すべきではないかと見えるが,それはそれとして.)
mituo

2021/12/10 02:02

求めている実行結果は観客も含めた全体の映像が表示された上でテニスコート内だけで差分処理が行われたものを表示したいと考えています。 しかし今の実行結果だとコートの左上の部分の画像だけがただ表示されるようになってしまいました。 プログラム初心者なためあまり理解してないままで修正してしまったため、うまく説明できなく 申し訳ございません。 isCourtにハードコーディングされている座標値の修正を行えば上記のような動作ができるのでしょうか? 詳しく修正方法をお聞きしたいです。 申し訳ございませんがよろしくお願いいたします。
tiitoi

2021/12/10 02:19

インデントが崩れてコードが読めないため、2つ目のコメントにあるコードを質問欄を編集して記載していただけますか? あと、質問欄に理想と現状こうなっているという2つの画像を貼っていただけますか? コードを見てもどのような画像に対して処理を行おうとしているのか不明のため、具体的なアドバイスは困難です。
mituo

2021/12/10 02:49

修正させていただきました。コードは読めますでしょうか? 説明が分かりにくかったら申し訳ございません。 よろしくお願い致します。
mituo

2021/12/10 03:10

何度も申し訳ございません。よろしくお願い致します。
fana

2021/12/10 03:12 編集

> しかし今の実行結果だとコートの左上の部分の画像だけが… まず,何をもって,現状が「コートの左上の部分」だと判断したのでしょう? …というところから突き詰めていく(疑ってちゃんと確認する)必要があるのではありませんか? 緑の線はあなたが勝手に固定の座標値を用いて描画しているだけのものなのですよね? 少なくとも提示された画像には他の(処理対象が元の映像のどこら辺になったのか?を判断できるような)情報があるようには見えません. (画像の一部を切り出したりしたならば,まずは単にそれを表示してみる,というところからやってますか?)
guest

回答1

0

img, p_temp, a_temp, ball_temp = detect(gray_diff)

この img を imshow() で表示していますが、これは切り抜いたあとの画像ですよね?
切り抜いた画像に対して、処理を行うのはいいのですが、

  • それで得られた座標に [xmin, ymin] を加算して、切り抜く前の画像の座標にする
  • 描画や表示も切り抜く前の画像に対して行う

という必要があると思います。

今は切り抜いたあとの画像に対して、元の切り抜く前の画像に対する描画を行っているので、意図しない結果になっていると思われます。

投稿2021/12/10 03:16

編集2021/12/10 03:18
tiitoi

総合スコア21956

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mituo

2021/12/10 03:42

確かに切り抜く前の画像に対して処理を行なっていました。 修正方法があまり分からなく、def detect()において戻り値img2を返す前に cv2.findContours(img2,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE,offset=(xmin,ymin)) を打ったのですが、エラーが出てしまいました。 プログラム初心者なため修正すべき点が分からなく、適当な動作をさせてしまい申し訳ございません。 お手数ですがどの点を直すべきかお教えいただけませんか。
tiitoi

2021/12/10 03:49

元動画ってGoogleDriveかどこかにアップロードできますか? 動かしてみないとこれ以上の具体的なアドバイスは難しいです
fana

2021/12/10 04:00 編集

> 今は切り抜いたあとの画像に対して、元の切り抜く前の画像に対する描画を行っているので… もっと明確に述べるならば… あなたのプログラムに 元の画像 と その一部を切り抜いた画像 とがあるならば,画素位置を扱う座標系も2つあるということだ.(わざわざ言うまでもないことと思えるが) 座標系Aでの座標 と 座標系Bでの座標 という概念があるならば,2つの世界でデータ値を行き来させるためには座標変換を行わねばならない. あなたがやるべきことは, ・どのデータがどちらの座標系におけるものなのかを把握し, ・2つの世界の境界がどこにあるのかを把握し, ・境界をまたぐ際には座標を変換する. という至極当たり前のことだ. 自身で作った世界の仕組みだというのに,他者に教わるような話なのか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問