[OpenCV]円の検出はハフ変換より最小外接円がいいぞ
赤いボールを検出しようとした話。
巷ではハフ変換を使った方法がある。
これ説明するのは難しいけど、この辺がわかりやすいかな。↓↓
でも、ハフ変換は処理が重い上に精度が悪い。なーんかもたつくし、できたとしても検出された円の大きさが安定しない。
なんだかなぁと思っていると、経験ある友達が「最小外接円を使った方がいい」と教えてくれた。
実際使ってみたところ、はるかに精度いい(厳密には違うが)しスムーズだった。
「精度がいい」というよりは、円を検出してるわけじゃないから認識が簡単になっていて、精度よく検出できているように感じる、と言った方が正確かな。
デモの画像を…と思ったけど手元にボールがなかったので、スマホにボールを表示させてそれを認識させてみた。
悪くないでしょう?
もちろんハフ変換が最適な場合もあると思うけど、今回は赤いボール1つだけ検出できればよかったから、最小外接円方式で十分快適だった。
検出に使うコード例を下に置いておく。
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 | # -*- coding: utf-8 -*- import numpy as np import cv2 def getCircle(frame, lower_color, upper_color): MIN_RADIUS = 25 # HSVによる画像情報に変換 hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # ガウシアンぼかしを適用して、認識精度を上げる blur = cv2.GaussianBlur(hsv, (9, 9), 0) # 指定した色範囲のみを抽出する color = cv2.inRange(blur, lower_color, upper_color) # オープニング・クロージングによるノイズ除去 element8 = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], np.uint8) oc = cv2.morphologyEx(color, cv2.MORPH_OPEN, element8) oc = cv2.morphologyEx(oc, cv2.MORPH_CLOSE, element8) # 輪郭抽出 img, contours, hierarchy = cv2.findContours(oc, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) print("{} contours.".format(len(contours))) if len(contours) > 0: # 一番大きい赤色領域を指定する contours.sort(key=cv2.contourArea, reverse=True) cnt = contours[0] # 最小外接円を用いて円を検出する (x, y), radius = cv2.minEnclosingCircle(cnt) center = (int(x), int(y)) radius = int(radius) # 円が小さすぎたら円を検出していないとみなす if radius < MIN_RADIUS: return None else: return center, radius else: return None if __name__ == '__main__': # 内蔵カメラを起動(カメラが一つしか繋がっていない場合は、引数に0を渡せば良い) cap = cv2.VideoCapture(0) while True: # 赤色の円を抽出する frame = cap.read()[1] getframe = getCircle(frame, np.array([130, 80, 80]), np.array([200, 255, 255])) if getframe is not None: # 見つかった円の上に青い円を描画 # getframe[0]:中心座標、getframe[1]:半径 cv2.circle(frame, getframe[0], getframe[1], (255, 0, 0), 2) print(getframe[1]) # 検出結果とともに映像を表示 cv2.imshow('Circle Detect', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break # 終了時にカメラを解放 cap.release() cv2.destroyAllWindows() |
みんな、最小外接円、使おうぜ!
この記事へのコメントはこちら