こんにちは.若尾です.
今回は前回のトラッキングを発展させて「複数物体の認識」と「複数色の認識」を行いました.今時,AI無しでやってるのもどうかと思いますが,クラシックなシステムも楽しんでいきましょう.
SSLの真似事とかやってみる方が面白味あるんですかね.教えてくださいセンスのある人.
前の記事
目次
対象読者
1. 複数物体の認識
2. コード解説
3. 複数色の認識
4. コード解説
5. まとめ
対象読者
- OpenCV 初心者
- RaspberryPi で画像処理をしたい人
- カラートラッキングを発展させたい人
- ロボット制御へ応用したい人
1. 複数物体の認識
できること
- 同じ色の物体を複数個検出
- それぞれを個別に矩形表示
- 中心座標表示
- 物体番号表示
単一物体追跡との違い
前回のコードでは,
largest = max(contours, key=cv2.contourArea)
としていたため,「最も大きい物体1つ」のみを追跡していました.
今回は,全ての輪郭に対して処理を行うことで,複数物体を追跡します.
ファイル作成
nano multi_tracking.py
コード
import cv2
import numpy as np
drawing = False
ix, iy = -1, -1
selected = False
hsv_lower = None
hsv_upper = None
frame_for_select = None
def select_color(event, x, y, flags, param):
global ix, iy, drawing
global hsv_lower, hsv_upper
global selected
global frame_for_select
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
ix, iy = x, y
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
x1, y1 = min(ix, x), min(iy, y)
x2, y2 = max(ix, x), max(iy, y)
roi = frame_for_select[y1:y2, x1:x2]
if roi.size == 0:
return
hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
h_mean = np.mean(hsv_roi[:, :, 0])
s_mean = np.mean(hsv_roi[:, :, 1])
v_mean = np.mean(hsv_roi[:, :, 2])
hsv_lower = np.array([
max(h_mean - 15, 0),
max(s_mean - 60, 50),
max(v_mean - 60, 50)
])
hsv_upper = np.array([
min(h_mean + 15, 179),
min(s_mean + 60, 255),
min(v_mean + 60, 255)
])
selected = True
print("HSV Lower:", hsv_lower)
print("HSV Upper:", hsv_upper)
cap = cv2.VideoCapture(0)
cv2.namedWindow("Multi Tracking")
cv2.setMouseCallback("Multi Tracking", select_color)
while True:
ret, frame = cap.read()
if not ret:
break
frame = cv2.resize(frame, (640, 480))
frame_for_select = frame.copy()
if selected:
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, hsv_lower, hsv_upper)
kernel = np.ones((5, 5), np.uint8)
mask = cv2.morphologyEx(
mask,
cv2.MORPH_OPEN,
kernel
)
mask = cv2.morphologyEx(
mask,
cv2.MORPH_CLOSE,
kernel
)
contours, _ = cv2.findContours(
mask,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE
)
object_id = 0
for contour in contours:
area = cv2.contourArea(contour)
# 小さいノイズ除去
if area < 500:
continue
x, y, w, h = cv2.boundingRect(contour)
cx = x + w // 2
cy = y + h // 2
# 矩形
cv2.rectangle(
frame,
(x, y),
(x + w, y + h),
(0, 255, 0),
2
)
# 中心点
cv2.circle(
frame,
(cx, cy),
5,
(0, 0, 255),
-1
)
# ID表示
cv2.putText(
frame,
f"ID:{object_id}",
(x, y - 30),
cv2.FONT_HERSHEY_SIMPLEX,
0.6,
(255, 0, 0),
2
)
# 座標表示
cv2.putText(
frame,
f"({cx}, {cy})",
(x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX,
0.6,
(0, 255, 0),
2
)
object_id += 1
cv2.imshow("Multi Tracking", frame)
key = cv2.waitKey(1)
if key == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
実行
python multi_tracking.py
2. コード解説
最大輪郭を使わない
largest = max(contours, key=cv2.contourArea)
単一物体追跡では,上記の通り処理していました.
これにより,最も大きい物体のみを取得していました.
全輪郭を処理
for contour in contours:
全ての輪郭を処理しています.
面積フィルタ
if area < 500:
continue
小さなノイズまで追跡しないため,一定以上の面積のみを対象にしています.
ID付与
object_id += 1
各物体に番号を振っています.
3. 複数色の認識
できること
- 複数色を同時認識
- 色ごとに矩形表示
- 中心座標表示
- 色ラベル表示
前回からの変更点
前回までは,
1種類のHSV範囲
のみを使用していました.
今回は,
赤
緑
青
それぞれに対してHSV範囲を設定し,複数色を同時検出します.
ファイル作成
nano multi_color_tracking.py
コード
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
# 色範囲(HSV)
# 赤
red_lower = np.array([0, 120, 70])
red_upper = np.array([10, 255, 255])
# 緑
green_lower = np.array([40, 50, 50])
green_upper = np.array([80, 255, 255])
# 青
blue_lower = np.array([100, 100, 50])
blue_upper = np.array([130, 255, 255])
colors = [
("Red", red_lower, red_upper, (0, 0, 255)),
("Green", green_lower, green_upper, (0, 255, 0)),
("Blue", blue_lower, blue_upper, (255, 0, 0))
]
while True:
ret, frame = cap.read()
if not ret:
break
frame = cv2.resize(frame, (640, 480))
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
kernel = np.ones((5, 5), np.uint8)
for color_name, lower, upper, draw_color in colors:
# 色抽出
mask = cv2.inRange(hsv, lower, upper)
# ノイズ除去
mask = cv2.morphologyEx(
mask,
cv2.MORPH_OPEN,
kernel
)
mask = cv2.morphologyEx(
mask,
cv2.MORPH_CLOSE,
kernel
)
contours, _ = cv2.findContours(
mask,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE
)
for contour in contours:
area = cv2.contourArea(contour)
if area < 500:
continue
x, y, w, h = cv2.boundingRect(contour)
cx = x + w // 2
cy = y + h // 2
# 矩形
cv2.rectangle(
frame,
(x, y),
(x + w, y + h),
draw_color,
2
)
# 中心点
cv2.circle(
frame,
(cx, cy),
5,
draw_color,
-1
)
# 色名表示
cv2.putText(
frame,
color_name,
(x, y - 30),
cv2.FONT_HERSHEY_SIMPLEX,
0.7,
draw_color,
2
)
# 座標表示
cv2.putText(
frame,
f"({cx}, {cy})",
(x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX,
0.6,
draw_color,
2
)
cv2.imshow("Multi Color Tracking", frame)
key = cv2.waitKey(1)
if key == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
実行
python multi_color_tracking.py
4. コード解説
色範囲を複数定義
red_lower = np.array([0, 120, 70])
red_upper = np.array([10, 255, 255])
今回は,色ごとに HSV範囲を定義しています.
colors リスト
colors = [
("Red", red_lower, red_upper, (0, 0, 255)),
]
以下の情報をまとめています.
- 色名
- HSV下限
- HSV上限
- 描画色(BGR)
for文で色ごと処理
for color_name, lower, upper, draw_color in colors:
各色に対して,
- 色抽出
- 輪郭検出
- 描画
を行っています.
描画色
draw_color
OpenCVでは BGR 順です.
(0, 0, 255) → 赤
(0, 255, 0) → 緑
(255, 0, 0) → 青
となります.
赤色検出の注意点
赤色は HSV 空間で両端に存在するため,環境によっては追加範囲が必要です.
例えば:
red_lower2 = np.array([170, 120, 70])
red_upper2 = np.array([179, 255, 255])
を追加して,2つのマスクを合成する方法もあります.
5. まとめ
今回は OpenCV を用いて,
- 複数物体の同時追跡
- 複数色の同時認識
を行いました.
単一物体追跡では,
largest = max(contours, key=cv2.contourArea)
によって最大物体のみを対象としていましたが,
for contour in contours:
を使用することで,複数物体を同時に処理できるようになりました.
また,HSV範囲を複数用意することで,色ごとの認識も実装できました.
次回は,
- ロボットアームの手先座標の取得
- 固定したマーカに手先を近づける
- 動いているボールを手先で追跡する
を実施したいと思います.

