1. 概要
PythonのOpenCVを使って特定の色の物体の座標を検出するプログラムのサンプルを書いた.青色の物体がどこにあるかを検出できるようにしてあります.手法は以下のような手順です
- HSV色空間に変換して
- 青色を部分を抽出して
- 抽出した各部分をラベリングして
- ラベリングした塊の面積,座標を計算
- 最も面積の大きい座標を返す
2. ソースコード
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import cv2 | |
import numpy as np | |
# 0 <= h <= 179 (色相) OpenCVではmax=179なのでR:0(180),G:60,B:120となる | |
# 0 <= s <= 255 (彩度) 黒や白の値が抽出されるときはこの閾値を大きくする | |
# 0 <= v <= 255 (明度) これが大きいと明るく,小さいと暗い | |
# ここでは青色を抽出するので120±20を閾値とした | |
LOW_COLOR = np.array([100, 75, 75]) | |
HIGH_COLOR = np.array([140, 255, 255]) | |
# 抽出する青色の塊のしきい値 | |
AREA_RATIO_THRESHOLD = 0.005 | |
def find_specific_color(frame,AREA_RATIO_THRESHOLD,LOW_COLOR,HIGH_COLOR): | |
""" | |
指定した範囲の色の物体の座標を取得する関数 | |
frame: 画像 | |
AREA_RATIO_THRESHOLD: area_ratio未満の塊は無視する | |
LOW_COLOR: 抽出する色の下限(h,s,v) | |
HIGH_COLOR: 抽出する色の上限(h,s,v) | |
""" | |
# 高さ,幅,チャンネル数 | |
h,w,c = frame.shape | |
# hsv色空間に変換 | |
hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV) | |
# 色を抽出する | |
ex_img = cv2.inRange(hsv,LOW_COLOR,HIGH_COLOR) | |
# 輪郭抽出 | |
_,contours,hierarchy = cv2.findContours(ex_img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) | |
# 面積を計算 | |
areas = np.array(list(map(cv2.contourArea,contours))) | |
if len(areas) == 0 or np.max(areas) / (h*w) < AREA_RATIO_THRESHOLD: | |
# 見つからなかったらNoneを返す | |
print("the area is too small") | |
return None | |
else: | |
# 面積が最大の塊の重心を計算し返す | |
max_idx = np.argmax(areas) | |
max_area = areas[max_idx] | |
result = cv2.moments(contours[max_idx]) | |
x = int(result["m10"]/result["m00"]) | |
y = int(result["m01"]/result["m00"]) | |
return (x,y) | |
def test(): | |
img = cv2.imread("sample.jpg") | |
# 位置を抽出 | |
pos = find_specific_color( | |
img, | |
AREA_RATIO_THRESHOLD, | |
LOW_COLOR, | |
HIGH_COLOR | |
) | |
if pos is not None: | |
cv2.circle(img,pos,10,(0,0,255),-1) | |
cv2.imwrite("result.jpg",img) | |
def main(): | |
# webカメラを扱うオブジェクトを取得 | |
cap = cv2.VideoCapture(0) | |
while True: | |
ret,frame = cap.read() | |
if ret is False: | |
print("cannot read image") | |
continue | |
# 位置を抽出 | |
pos = find_specific_color( | |
frame, | |
AREA_RATIO_THRESHOLD, | |
LOW_COLOR, | |
HIGH_COLOR | |
) | |
if pos is not None: | |
# 抽出した座標に丸を描く | |
cv2.circle(frame,pos,10,(0,0,255),-1) | |
# 画面に表示する | |
cv2.imshow('frame',frame) | |
# キーボード入力待ち | |
key = cv2.waitKey(1) & 0xFF | |
# qが押された場合は終了する | |
if key == ord('q'): | |
break | |
cv2.destroyAllWindows() |
3. 結果
test()でsamle.jpgに対して処理を行います.またmain()でウェブカメラの画像をリアルタイムで処理します.以下はsample.jpgに処理を適用した結果です.
4.参考
- OpenCV - findContours() による輪郭抽出 - Pynote
- OpenCV - 輪郭を近似する、面積を求める、Bounding Box を求める方法について - Pynote
- Python, OpenCVで画像ファイルの読み込み、保存(imread, imwrite) | note.nkmk.me
昨日からPythonの学習を始めブログを拝見している者です。
返信削除Opencvライブラリを用い、やりたかった内容と酷似していた為、
とりあえず記載頂いているソースコードを読み解こうと思ったのですが
上手く実行することができません。
差し障りなければ以下お答え願えませんでしょうか。
状況としてはステップオーバー(1行ずつ実行した際に
defの中身とtestの中身を飛ばして実行しているように見えます、結果は出力されません)
1.frame(14行目)には何か指定しなければならないのでしょうか
2.img = cv2.imread("sample.jpg") (51行目)をCドライブ内で
絶対参照で指定していますがこれは問題でしょうか。(imwriteも同様)
3.他に当方が指定する項目はありますでしょうか
初心者の質問で分かり辛く申し訳ありませんが宜しくお願い致します。