フィンガロンの位置と方向の検出は、赤と青のマーカーを利用しています。動画での、u, v は二つのマーカーの画像上での座標(左上が0, 0)、dig は、上を0度とし左回りを正とした方向、h, v は水平方向と垂直方向の単位ベクトルを表しています。
単独の色の検出方法は、こちらにまとめています。
05 画像から特定の色領域を抽出
09 リアルタイムで特定の色をトラッキングする(動画)
では、フィンガロンで使った色の検出のプログラムの内容です。
まず、LOWとHIGHの間の色領域の中心(cu, cv)を返す関数を作りました。
1 |
ret, (cu, cv), img_out = get_colcenter(img, img_out, LOW, HIGH) |
そして、このget_colcenter を使って、中心位置、水平ベクトル、垂直ベクトル、方向を出力する関数を作りました。
1 |
ret, center_pos, hvec, vvec, dig, img_out = get_posdirec(img, img_out) |
実際に使ったプログラムは以下の通りです。
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 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
#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 |