OpenCVで特定の色の位置を取得するサンプルです。精度がよくなく、おおざっぱな方法のため、使える場面は限定されるかもしれません。
青・緑・赤のボールだけ認識
吊るされたコットンボールの中で、青、緑、赤のみを認識させます。
認識した場合、緑の円が描画されます。
精度はあまり高くなく、作ったサンプルコードでは、1色につき1つまでしかボールを認識できない制限もあります。
環境・バージョン
Windows10
Python 3.6.4
OpenCV3.4.0
2018年末にOpenCV4が出ているようです。OpenCV3でしか動作確認していないので、ご注意ください。
ボールの色認識のコード
昔書いたボールの輪郭抽出のコードを少し作り変えています。
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] mask = np.zeros(h.shape, dtype=np.uint8) mask[((h < lower[0]*-1) | (h > upper[0])) & (s > lower[1])] = 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: cnt = contours[0] # 最小外接円を描く (x,y), radius = cv2.minEnclosingCircle(cnt) center = (int(x),int(y)) radius = int(radius) if radius > r: return cv2.circle(frame,center,radius,(0,255,0),2) else: return frame else: return frame # 青・緑・赤マスク res_blue = getMask([110,45,30], [150,255,255]) res_green = getMask([20,35,30], [50,255,255]) res_red = getMask([-10,10,30], [150,255,255]) #輪郭取得 getContours(res_blue, 30, 75) # (画像, 明度閾値, 最小半径) contours_frame = getContours(res_green, 30, 75) contours_frame = getContours(res_red, 30, 75) # 再生する場合 cv2.imshow('video',contours_frame) k = cv2.waitKey(25) & 0xFF #Q で終了 if k == ord('q'): break cv2.destroyAllWindows() |
認識する色の範囲や、最小半径は、扱う映像に合わせて調整してください。
コードの53~55行目で、各色のマスクを作っています。
getMask([最小の色相,彩度,明度], [最大の色相,彩度,明度])で抽出したい色の範囲を指定します。(ただし赤のみ諸事情で最小明度と最大明度・彩度の値が無視されます。)
上記コードでは、例えば青のマスクの場合、色相110~150の間、彩度45~255、明度30~255を取得しています。
そして58~60行目で、各色でマスクされた画像を元に、輪郭(位置)を取得します。
getContoursの最後の引数(75)が、認識する最小半径の指定です。より小さいものを認識するときは、値を小さくします。
ただし、小さいほどノイズに反応して、誤認されるかもしれません。
ありがとうございます参考になります
赤色の丸だけを複数認識するとなるとどうなりますでしょうか?
気になったので質問します
38行目のcnt = contours[0]で、一番大きい面積(ボール)を1つ抽出しています。
2つ認識させる場合は、contours[1]、3つならcontours[2]・・・と、forで繰り返すと複数認識になるかと思います。
週末あたり余力あれば、コードを書いて掲載します。
あと全部赤色だった場合輪郭の円を描きたいです
動画でなく画像で、
白を検出できますか。
お願いします。
下記でできると思います。[200, 200, 200]の数字が大きいほど検出する白の範囲が狭まります。
import cv2
import numpy as np
image = cv2.imread('./test1.png') # ファイル読み込み
# BGRでの色抽出
bgrLower = np.array([200, 200, 200]) # 抽出する色の下限(BGR)
bgrUpper = np.array([255, 255, 255]) # 抽出する色の上限(BGR)
img_mask = cv2.inRange(image, bgrLower, bgrUpper) # BGRからマスクを作成
result = cv2.bitwise_and(image, image, mask=img_mask) # 元画像とマスクを合成
参考:
【python/OpenCV】画像の特定の色を抽出する方法