昔作ったOpenCVでのボール認識を微調整しました。赤色のボールを複数認識させています。正確にはボール認識ではなく、色のみを認識する単純なプログラムです。
赤いボールに緑の丸を付ける
映像の中に赤いボールを見つけらたら、緑の丸を付けるようにします。
一定の面積以上の赤色を見つけたら、その周囲に緑色の円を描くという、単純な処理です。
赤色を見つける際、映像によっては茶色やベージュなどの赤みを含むものを誤認するかもしれません。その場合は認識させたい赤の色相、彩度、明度の範囲を調整します。
環境・バージョン
Windows10
Python 3.6.4
OpenCV3.4.0
赤色ボールの検出コード
最小半径30px以上のすべての赤色(ボール)に、緑色の輪郭を描画します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
import cv2 import numpy as np cap = cv2.VideoCapture('xxxx.mp4') # 任意の動画 while(1): _, frame = cap.read() #マスク画像取得 def getMask(l, u): # HSVに変換 hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower = np.array(l) upper = np.array(u) if lower[0] >= 0: #色相が正の値のとき、赤以外のマスク mask = cv2.inRange(hsv, lower, upper) else: #色相が負の値のとき、赤用マスク h = hsv[:, :, 0] s = hsv[:, :, 1] v = hsv[:, :, 2] mask = np.zeros(h.shape, dtype=np.uint8) mask[((h < lower[0]*-1) | h > upper[0]) & (s > lower[1]) & (s < upper[1]) & (v > lower[2]) & (v < upper[2])] = 255 return cv2.bitwise_and(frame,frame, mask= mask) # 輪郭取得 def getContours(img,t,r): gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) ret, thresh = cv2.threshold(gray, t, 255, cv2.THRESH_BINARY) imgEdge, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 一番大きい輪郭を抽出 contours.sort(key=cv2.contourArea, reverse=True) #一つ以上検出 if len(contours) > 0: for cnt in contours: # 最小外接円を描く (x,y), radius = cv2.minEnclosingCircle(cnt) center = (int(x),int(y)) radius = int(radius) if radius > r: radius_frame = cv2.circle(frame,center,radius,(0,255,0),2) return radius_frame else: return frame # マスクの最小HSVと最大HSVを指定 ただし赤の場合は最小Hを負の値にする(独自ルール) # 青マスク H110~150、S45~255、V100~255 #res_blue = getMask([110,45,100], [150,255,255]) # 赤マスク H0~10または170~180、S50~255、V200~255 res_red = getMask([-10,50,200], [170,255,255]) #輪郭取得 contours_frame = getContours(res_red, 50, 30) # (画像, 明度閾値, 最小半径) # 再生 cv2.imshow('video',contours_frame) k = cv2.waitKey(25) & 0xFF #Q で終了 if k == ord('q'): break cv2.destroyAllWindows() |
赤色はH(色相)が分断されているため、例えば「0~10または170~180」という条件になります。
赤色の判定は、コードが少し複雑になります。
赤色抽出の確認
コード64行目を、cv2.imshow(‘video’,res_red)と変えることで、赤色が抽出された映像を確認できます。
ちょうど良いHSVを探ってください。
やはり、この方法では正確にボールの位置を検出するような使い方には向いていません。
おおざっぱに検出して楽しむ(?)時の、参考になればと思います。
質問します。
Raspberry Pi4のPicameraを使用して動画を撮影して、撮影しながら赤色かつボールだけを取得したいと思っています。
参考させていただきながら作ってみましたがうまくいきません教えていただきたいですお願いします。
いかにソースコードを示します。
import numpy as np
import cv2
import time
cap = cv2.VideoCapture(0)
while(1):
re, frame = cap.read()
original = frame.copy()
def getMask(l, u):
# HSV変換
image = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 指定した色の下限
lower = np.array(l)#([0, 208, 94], dtype=”uint8″)
# 指定した色の上限
upper = np.array(u)#([179, 255, 232], dtype=”uint8″)
# 指定した色の画素を 255、それ以外の画素を0として2値化を行う関数
mask = cv2.inRange(image, lower, upper)
return mask
def getContours(mask):
# 輪郭抽出
cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# バージョンに応じて輪郭抽出する
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
if len(cnts) > 0:
# 等高線を反復処理し、頂点の数でフィルタリングする
for c in cnts:
# 周囲長
perimeter = cv2.arcLength(c, True)
# 輪郭の近似
approx = cv2.approxPolyDP(c, 0.04 * perimeter, True)
if len(approx) > 6:
umbrella=cv2.drawContours(original, [c], -1, (36, 255, 12), -1)
return umbrella
else:
return original
mask = getMask([-20 ,100, 94], [180, 255, 232])
cv2.imshow(‘mask’, mask)
cv2.imshow(‘original’, original)
umbrella_frame = getContours(mask)
cv2.imshow(‘umbrellaCnt’, umbrella_frame)
key = cv2.waitKey(1) & 0xFF
if key == ord(‘c’):
cv2.imwrite(‘mask.png’, mask)
cv2.imwrite(‘original.png’, original)
elif key == ord(‘q’):
break
cv2.destroyAllWindows()
追記します
リアルタイムでの赤色とボールの検出です。
頂いたコードを貼り付けて実行してみると、カメラ映像から赤色の物体がなくなったときにエラーで止まりました。
症状は同じでしょうか?
問題を特定できていませんが、私のコードの不備かもしれません。
回答ありがとうございます。
同じく赤い物体がなくなるとエラーになります
その原因はなぜでしょうか?
リアルタイムで取得することがよろしくない?
リアルタイムは関係なさそうでした。
28行目のif len(cnts) > 0:で、輪郭抽出できるものが0個の時(else:)の処理がないことが原因のようです。
37行目の後に、
else:
return original
を追加すると、いかがでしょうか?