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

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

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

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

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

Q&A

解決済

1回答

19686閲覧

選択した範囲だけを検出したい

snake207

総合スコア13

OpenCV

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

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

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

0グッド

0クリップ

投稿2018/10/10 11:18

###やりたいこと
決められた範囲だけを背景差分法で動体物(テニスボール)を検出したい

質問内容

自分で決めた範囲だけを処理するにはどうすればいいですか?

現状

マウスで範囲を選択してボール検出(ボール以外も検出していますが...)ができるプログラムを作成しました。
しかし、マウスで選択した範囲だけ処理するというのができなかったため、自分で検出したい範囲(座標)を決めて、
その範囲だけを処理するプログラムに変更することにしました。そこで、処理する範囲をどのようなコードで決めて
処理をすればいいか分からない状態です。
※作成したプログラムは下記に載せておきます。

プログラム

Python

1# -*- coding: utf-8 -*- 2 3import numpy as np 4 5import cv2 6 7from time import time 8 9import subprocess 10 11 12 13VIDEO_DATE = "tennis.AVI" 14 15WINDOW_NAME = "MouseEvent" 16 17 18 19def dilation(dilationSize, kernelSize, img): # 膨張した画像にして返す 20 21 kernel = np.ones((kernelSize, kernelSize), np.uint8) 22 23 element = cv2.getStructuringElement(cv2.MORPH_RECT, (2 * dilationSize + 1, 2 * dilationSize + 1), (dilationSize, dilationSize)) 24 25 dilation_img = cv2.dilate(img, kernel, element) 26 27 return dilation_img 28 29 30 31 32def detect(gray_diff, thresh_diff=95, dilationSize=9, kernelSize=20): # 一定面積以上の物体を検出 33 34 retval, black_diff = cv2.threshold(gray_diff, thresh_diff, 255, cv2.THRESH_BINARY) # 2値化 35 36 dilation_img = dilation(dilationSize, kernelSize, black_diff) # 膨張処理 37 38 img = dilation_img.copy() 39 40  image, contours, hierarchy = cv2.findContours(dilation_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 境界線検出 41 42 43  44 ball_pos = [] 45 46 47 48 for i in range(len(contours)): # 重心位置を計算 49 50 count = len(contours[i]) 51 52 53 area = cv2.contourArea(contours[i]) # 面積計算 54 55 x, y = 0.0, 0.0 56 57 for j in range(count): 58 59 x += contours[i][j][0][0] 60 61 y += contours[i][j][0][1] 62 63 64 65 66 x /= count 67 68 y /= count 69 70 x = int(x) 71 72 y = int(y) 73 74 ball_pos.append([x, y]) 75 76 77 return ball_pos, img 78 79 80 81 82def displayCircle(image, ballList, thickness=5): 83 84 for i in range(len(ballList)): 85 86 x = int(ballList[i][0]) 87 88 y = int(ballList[i][1]) 89 90 cv2.circle(image, (x, y), 10, (0, 0, 255), thickness) 91 92 return image 93 94 95 96 97def resizeImage(image, w=2, h=2): 98 99 height = image.shape[0] 100 101 width = image.shape[1] 102 103 resizedImage = cv2.resize(image, (int(width / w), int(height / h))) 104 105 return resizedImage 106 107 108 109 110def blackToColor(bImage): 111 112 colorImage = np.array((bImage, bImage, bImage)) 113 114 colorImage = colorImage.transpose(1, 2, 0) 115 116 return colorImage 117 118 119 120class PointList(): 121 122 def __init__(self, npoints): 123 124 self.video = cv2.VideoCapture(VIDEO_DATE) 125 126 self.frame = None 127 128 self.npoints = npoints 129 130 self.ptlist = np.empty((npoints, 2), dtype=int) 131 132 self.pos = 0 133 134 cv2.setMouseCallback(WINDOW_NAME, self.onMouse) 135 136 137 138 def add(self, x, y): 139 140 if self.pos < self.npoints: 141 142 self.ptlist[self.pos, :] = [x, y] 143 144 self.pos += 1 145 146 return True 147 148 return False 149 150 151 152 def run(self): 153 154 ok, self.frame = self.video.read() # 最初のフレームを読み込む 155 156 if not ok: 157 158 print('Cannot read video file') 159 160 sys.exit() 161 162 self.frame_pre = self.frame.copy() 163 164 165 166 while True: 167 168 ok, self.frame = self.video.read() # フレームを読み込む 169 170 if not ok: 171 172 break 173 174 self.frame_next = self.frame.copy() 175 176 177 178 if(self.pos == self.npoints): 179 180 cv2.line(self.frame, (self.ptlist[0][0], self.ptlist[0][1]), 181 182 (self.ptlist[1][0], self.ptlist[1][1]), (0, 255, 0), 3) 183 184 cv2.line(self.frame, (self.ptlist[1][0], self.ptlist[1][1]), 185 186 (self.ptlist[2][0], self.ptlist[2][1]), (0, 255, 0), 3) 187 188 cv2.line(self.frame, (self.ptlist[2][0], self.ptlist[2][1]), 189 190 (self.ptlist[3][0], self.ptlist[3][1]), (0, 255, 0), 3) 191 192 cv2.line(self.frame, (self.ptlist[3][0], self.ptlist[3][1]), 193 194 (self.ptlist[0][0], self.ptlist[0][1]), (0, 255, 0), 3) 195 196 color_diff = cv2.absdiff(self.frame_next, self.frame_pre) # フレーム間の差分計算 197 198 gray_diff = cv2.cvtColor(color_diff, cv2.COLOR_BGR2GRAY) # グレースケール変換 199 200 retval, black_diff = cv2.threshold(gray_diff,30, 255, cv2.THRESH_BINARY) 201 202 203 204 ball, dilation_img = detect(gray_diff) 205 206 207 208 self.frame = displayCircle(self.frame, ball, 2) # 丸で加工 209 210 cImage = blackToColor(dilation_img) # 2値化画像をカラーの配列サイズと同じにする 211 212 213 214 self.frame_pre = self.frame_next.copy() # 次のフレームの読み込み 215 216 217 218 cv2.imshow(WINDOW_NAME, self.frame) 219 220 if cv2.waitKey(10) ==27: # Escキーで抜ける 221 222 break 223 224 225 226 def onMouse(self,event, x, y, flag, params): 227 228 if event == cv2.EVENT_MOUSEMOVE: # マウスが移動したときにx線とy線を更新する 229 230 self.frame2 = np.copy(self.frame) 231 232 h, w = self.frame2.shape[0], self.frame2.shape[1] 233 234 cv2.line(self.frame2, (x, 0), (x, h - 1), (255, 0, 0)) 235 236 cv2.line(self.frame2, (0, y), (w - 1, y), (255, 0, 0)) 237 238 cv2.imshow(WINDOW_NAME, self.frame2) 239 240 241 242 if event == cv2.EVENT_LBUTTONDOWN: # レフトボタンをクリックしたとき、ptlist配列にx,y座標を格納する 243 244 if self.add(x, y): 245 246 print('[%d] ( %d, %d )' % (ptlist.pos - 1, x, y)) 247 248 cv2.circle(self.frame, (x, y), 3, (0, 0, 255), 3) 249 250 cv2.imshow(WINDOW_NAME, self.frame) 251 252 else: 253 254 print('All points have selected. Press ESC-key.') 255 256 257 258if __name__ == '__main__': 259 260 cv2.namedWindow(WINDOW_NAME) 261 262 npoints = 4 263 264 ptlist = PointList(npoints) 265 266 ptlist.run() 267 268 cv2.destroyAllWindows()

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

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

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

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

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

guest

回答1

0

ベストアンサー

選択した範囲というのは長方形という認識でよいでしょうか?
以下の手順で指定した範囲だけ背景差分処理を行えます。

手順

1. 長方形を左上の点、右下の点で定義する。

python

1xmin, xmax = 200, 600 2ymin, ymax = 100, 400 3 4 5(xmin, ymin)+-----+ 6 | | 7 | | 8 +-----+(xmax, ymax)

2. 長方形でフレームを切り取る。

python

1prev_frame[ymin:ymax, xmin:xmax] 2frame[ymin:ymax, xmin:xmax]

3. 切り取った画像に対して、背景差分に必要な各種処理を行う。

4. findContours() で輪郭抽出を行う際に offset 引数を渡す。

切り取った画像に対して輪郭抽出しているので、そのままではもとのフレームの座標とは位置がずれてしまう。
offset=(xmin, ymin) と渡してあげると、検出した輪郭点にこの offset を足した値が返り値として返ってくる。

python

1cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE, 2 offset=(xmin, ymin))

サンプルコード

サンプルに使用した動画 vtest.avi

python

1import matplotlib.pyplot as plt 2import numpy as np 3import cv2 4 5# Video Reader を作成 6cap = cv2.VideoCapture('vtest.avi') 7width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) 8height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) 9fps = cap.get(cv2.CAP_PROP_FPS) 10 11# Video Writer を作成 12fourcc = cv2.VideoWriter_fourcc(*'DIVX') 13writer = cv2.VideoWriter('output.avi', fourcc, fps, (width, height)) 14 15xmin, xmax = 200, 600 16ymin, ymax = 100, 400 17 18prev_frame = None 19while True: 20 ret, frame = cap.read() 21 if not ret: 22 break 23 24 if prev_frame is not None: 25 # BGR -> grayscale 26 prev_gray = cv2.cvtColor(prev_frame[ymin:ymax, xmin:xmax], cv2.COLOR_BGR2GRAY) 27 gray = cv2.cvtColor(frame[ymin:ymax, xmin:xmax], cv2.COLOR_BGR2GRAY) 28 # 差分を計算 29 diff = cv2.absdiff(gray, prev_gray) 30 # 閾値処理 31 _, binary = cv2.threshold(diff, 20, 255, cv2.THRESH_BINARY) 32 # 輪郭抽出 33 _, contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE, 34 offset=(xmin, ymin)) 35 # 面積でフィルタリング 36 contours = list(filter(lambda cnt: cv2.contourArea(cnt) > 1000, contours)) 37 # 輪郭を囲む長方形に変換 38 rects = [cv2.boundingRect(cnt) for cnt in contours] 39 # 長方形を描画する。 40 bgr = frame.copy() 41 for x, y, width, height in rects: 42 cv2.rectangle(bgr, (x, y), (x + width, y + height), (0, 255, 0), 2) 43 cv2.rectangle(bgr, (xmin, ymin), (xmax, ymax), (0, 0, 255), 2) 44 45 writer.write(bgr) 46 47 prev_frame = frame 48 49writer.release() 50cap.release() 51cv2.destroyAllWindows()

イメージ説明

背景差分について

cv2.absdiff() による前フレームとの単純な差分で背景差分を行うことはおすすめできません。理由は輝度変化などに弱いからです。

OpenCV ではより高度な 背景差分のアルゴリズム が利用できるので、そちらを使ったほうがよいです。

投稿2018/10/10 15:49

tiitoi

総合スコア21956

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

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

snake207

2018/10/10 21:42 編集

回答、ありがとうございます。 サンプルコードまで作成していただき、とても参考になります。 現在使っている背景差分法のアルゴリズムよりも高度なものがあるとは知りませんでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問