フィンガロンの位置と方向の検出は、赤と青のマーカーを利用しています。動画での、u, v は二つのマーカーの画像上での座標(左上が0, 0)、dig は、上を0度とし左回りを正とした方向、h, v は水平方向と垂直方向の単位ベクトルを表しています。
単独の色の検出方法は、こちらにまとめています。
05 画像から特定の色領域を抽出
09 リアルタイムで特定の色をトラッキングする(動画)
では、フィンガロンで使った色の検出のプログラムの内容です。
まず、LOWとHIGHの間の色領域の中心(cu, cv)を返す関数を作りました。
ret, (cu, cv), img_out = get_colcenter(img, img_out, LOW, HIGH)
そして、このget_colcenter を使って、中心位置、水平ベクトル、垂直ベクトル、方向を出力する関数を作りました。
ret, center_pos, hvec, vvec, dig, img_out = get_posdirec(img, img_out)
実際に使ったプログラムは以下の通りです。
#coding: utf-8
import cv2
import numpy as np
import time
import pdb
COL_G_LOW = [50, 104, 22]
COL_G_HIGH = [90, 255, 157]
COL_R_LOW = [[0, 170], 118, 64] # 赤は二つの範囲で定義
COL_R_HIGH =[[10, 180], 255, 160]
N_ERODE = 2
N_DILATE = 2
def get_colcenter(img, img_out, LOW, HIGH):
"""
LOW, HIGH の範囲に入る色領域で、
最も大きい領域の重心を出力
"""
cu, cv = (None, None)
ret = False
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # HSVに変換
if type(LOW[0]) is list:
lo = [LOW[0][0], LOW[1], LOW[2]]
hi = [HIGH[0][0], HIGH[1], HIGH[2]]
mask1 = cv2.inRange(img_hsv, np.array(lo), np.array(hi))
lo = [LOW[0][1], LOW[1], LOW[2]]
hi = [HIGH[0][1], HIGH[1], HIGH[2]]
mask2 = cv2.inRange(img_hsv, np.array(lo), np.array(hi))
mask = cv2.bitwise_or(mask1, mask2)
else:
mask = cv2.inRange(img_hsv, np.array(LOW), np.array(HIGH))
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
mask = cv2.erode(mask, kernel, iterations = N_ERODE)
mask = cv2.dilate(mask, kernel, iterations = N_DILATE)
conts = cv2.findContours(mask, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[1]
if len(conts)==0:
return False, (None, None), img_out
imax = None
img_out = cv2.drawContours(img_out, conts, -1, (0, 255, 0), 1)
MIN_AREA = 20
max_area = 0
for i, cnt in enumerate(conts): # 各輪郭線についての処理
#M=cv2.moments(cnt)
area = cv2.contourArea(cnt)
if area > MIN_AREA:
if max_area < area:
imax = i
max_area = area
if imax is None:
return False, (None, None), img_out
M=cv2.moments(conts[imax])
cu=int(M['m10']/M['m00'])
cv=int(M['m01']/M['m00'])
cv2.circle(img_out, (cu, cv), 5, (0,255,255), 2)
return True, (cu, cv), img_out
def get_posdirec(img, img_out):
"""
赤と青のマーカー位置から、
二つのマーカーの中心位置と方向を出力
hvec: 水平方向
vvec: 垂直方向
"""
rr, uvr, img_out = get_colcenter(img, img_out,
COL_R_LOW, COL_R_HIGH)
rg, uvg, img_out = get_colcenter(img, img_out,
COL_G_LOW, COL_G_HIGH)
if rr is False or rg is False:
return False, None, None, None, None, img_out
uvr = np.array(uvr)
uvg = np.array(uvg)
center_pos = (uvr + uvg) * 0.5
duv = uvg - uvr
dig = np.arctan2(-duv[1], duv[0]) / np.pi * 180
hvec = duv / np.linalg.norm(duv)
vvec = np.array([hvec[1], -hvec[0]])
return True, center_pos, hvec, vvec, dig, img_out
# テスト
if __name__ == '__main__':
cap = cv2.VideoCapture(0)
prev_t = 0
current_t = 0
cnt = 0
while True:
# fps の計算
current_t = time.time()
cnt += 1
if current_t - prev_t > 1.0:
dt = current_t - prev_t
prev_t = current_t
fps = cnt / dt
cnt = 0
print('fps = %.2f' % fps)
# カメラ画像表示
ret, img = cap.read()
if img is None:
print('no image')
time.sleep(1)
continue
if ret is False:
print('no image')
else:
img_out = img.copy()
ret, uv, hvec, vvec, dig, img_out \
= get_posdirec(img, img_out)
if ret is True:
cv2.putText(img_out,
'u %d, v %d, %.1f dig' % (uv[0], uv[1], dig),
(10, 40),
cv2.FONT_HERSHEY_SIMPLEX, 1.0,
(0, 0, 200), thickness=2)
cv2.putText(img_out,
'h(%.2f, %.2f), v(%.2f, %.2f)' % (hvec[0], hvec[1], vvec[0], vvec[1]),
(10, 80),
cv2.FONT_HERSHEY_SIMPLEX, 1.0,
(0, 0, 200), thickness=2)
cv2.imshow('img', img_out)
# ’q’ で終了
INPUT = cv2.waitKey(10) & 0xFF
if INPUT == ord('q'):
cap.release()
cv2.destroyAllWindows()
break