OpenCVには、簡単にARマーカーを扱うことのできる "aruco" というモジュールが用意されています。
本記事では、arucoの基本的な使い方をpythonのサンプルコードを用いながら紹介します。
本記事では、下記の内容を紹介しています。
この記事で分かること
- arucoを使う準備
- arucoを用いてマーカーを生成する方法
- arucoを用いてマーカーを検出する方法
- arucoを用いてマーカーの座標を抽出する方法
- arucoを用いてマーカーIDと動画を重ねて表示する方法
スポンサーリンク
arucoを使う準備:OpenCV-contribのインストール
arucoモジュールは、OpenCVの拡張モジュールであるOpenCV-contribをインストールすることで、使用可能になります。
pipの場合は、下記のコマンドでインストールできます。
$ pip install opencv-contrib-python
なお、OpenCVとバージョンを合わせる必要があるので、
OpenCVのバージョンを確認し、同じバージョンを指定してインストールします。
以下は、 3.4.5.20 のバージョンを指定した例です。
$ pip install opencv-contrib-python==3.4.5.20
ラズパイへOpenCV-contribをインストールする場合は、下記の記事を参考にしてみてください。
ラズパイ(Raspberry Pi)へのOpenCVのインストール方法・手順
arucoを用いてマーカーを生成
はじめに、マーカーの生成方法について紹介します。
arucoモジュールを用いてマーカー画像を生成し、画像として保存するコードを紹介します。
### arucoマーカーを生成して、画像として保存する
import cv2
from cv2 import aruco
import os
### --- parameter --- ###
# マーカーの保存先
dir_mark = r'C:\test'
# 生成するマーカー用のパラメータ
num_mark = 20 #個数
size_mark = 500 #マーカーのサイズ
### --- マーカーを生成して保存する --- ###
# マーカー種類を呼び出し
dict_aruco = aruco.Dictionary_get(aruco.DICT_4X4_50)
for count in range(num_mark) :
id_mark = count #countをidとして流用
img_mark = aruco.drawMarker(dict_aruco, id_mark, size_mark)
if count < 10 :
img_name_mark = 'mark_id_0' + str(count) + '.jpg'
else :
img_name_mark = 'mark_id_' + str(count) + '.jpg'
path_mark = os.path.join(dir_mark, img_name_mark)
cv2.imwrite(path_mark, img_mark)
出力例
前述のサンプルコードで、マーカーを生成した結果がこちらです。
20個のマーカーの画像が、testフォルダに保存されました。
注意事項
画像の保存先は、 dir_mark = r'C:\test' としています。
OpecCVは、画像の読み込み先や保存先に日本語が含まれているとエラーとなる場合があるので注意してください。
スポンサーリンク
arucoを用いてマーカーを検出する方法
次は、マーカーの検出方法について紹介します。
arucoモジュールを用いて、検出したマーカーのIDをリストで返すコードを紹介します。
### 検出したマーカーのIDをリストで返す
import cv2
from cv2 import aruco
import numpy as np
import time
class MarkSearch :
### --- aruco設定 --- ###
dict_aruco = aruco.Dictionary_get(aruco.DICT_4X4_50)
parameters = aruco.DetectorParameters_create()
def __init__(self, cameraID):
self.cap = cv2.VideoCapture(cameraID)
def get_markID(self):
"""
静止画を取得し、arucoマークのidリストを取得する
"""
ret, frame = self.cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
corners, ids, rejectedImgPoints = aruco.detectMarkers(gray, dict_aruco, parameters=parameters)
list_ids = np.ravel(ids)
return list_ids
if __name__ == "__main__" :
import cv2
from cv2 import aruco
import numpy as np
import time
### --- aruco設定 --- ###
dict_aruco = aruco.Dictionary_get(aruco.DICT_4X4_50)
parameters = aruco.DetectorParameters_create()
### --- parameter --- ###
cameraID = 0
cam0_mark_search = MarkSearch(cameraID)
try:
while True:
print(' ----- get_markID ----- ')
print(cam0_mark_search.get_markID())
time.sleep(0.5)
except KeyboardInterrupt:
cam0_mark_search.cap.release()
出力例
カメラ内に映り込んだ、マーカーのIDをリストで返します。
例えば、ID1~5のマーカーを5つ検出した場合は、下記のようになります。
[5 1 2 3 4]
解説
gray画像に変換されたカメラキャプチャ画像から、IDを抽出しているのが、下記の部分になります。
corners, ids, rejectedImgPoints = aruco.detectMarkers(gray, dict_aruco, parameters=parameters)
上記の返り値のうち、マーカーのIDは、 "ids" に格納されています。
idsは、下記のように、そのままでは使いづらい形となってます。
[[1]
[3]
[4]
[5]
[2]]
そのため、list_ids = np.ravel(ids) の部分で、
1次元のリストに変換しています。
キーボードからの中断処理をキャッチできるように、
whileループはtry文の中でまわしています。
try文を用いた例外処理については、下記の記事で紹介しています。
スポンサーリンク
arucoを用いてマーカーの座標を抽出する方法
次に、マーカーの座標を抽出する方法を紹介します。
arucoモジュールを用いて、指定したIDのマーカーが検出された場合に、座標をリストで返すコードを紹介します。
### マーカーの座標を抽出する
import cv2
from cv2 import aruco
import numpy as np
import time
class MarkSearch :
### --- aruco設定 --- ###
dict_aruco = aruco.Dictionary_get(aruco.DICT_4X4_50)
parameters = aruco.DetectorParameters_create()
def __init__(self, cameraID):
self.cap = cv2.VideoCapture(cameraID)
def get_mark_coordinate(self, num_id):
"""
静止画を取得し、所望のマークの座標を取得する
"""
ret, frame = self.cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
corners, ids, rejectedImgPoints = aruco.detectMarkers(gray, dict_aruco, parameters=parameters)
### num_id のマーカーが検出された場合 ###
if num_id in np.ravel(ids) :
index = np.where(ids == num_id)[0][0] #num_id が格納されているindexを抽出
cornerUL = corners[index][0][0]
cornerUR = corners[index][0][1]
cornerBR = corners[index][0][2]
cornerBL = corners[index][0][3]
center = [ (cornerUL[0]+cornerBR[0])/2 , (cornerUL[1]+cornerBR[1])/2 ]
print('左上 : {}'.format(cornerUL))
print('右上 : {}'.format(cornerUR))
print('右下 : {}'.format(cornerBR))
print('左下 : {}'.format(cornerBL))
print('中心 : {}'.format(center))
print(corners[index])
return center
return None
if __name__ == "__main__" :
import cv2
from cv2 import aruco
import numpy as np
import time
### --- aruco設定 --- ###
dict_aruco = aruco.Dictionary_get(aruco.DICT_4X4_50)
parameters = aruco.DetectorParameters_create()
### --- parameter --- ###
cameraID = 0
cam0_mark_search = MarkSearch(cameraID)
markID = 1
try:
while True:
print(' ----- get_mark_coordinate ----- ')
print(cam0_mark_search.get_mark_coordinate(markID))
time.sleep(0.5)
except KeyboardInterrupt:
cam0_mark_search.cap.release()
出力例
指定したIDのマーカーが検出された場合に、座標をリストで返します。
例えば、ID=1のマークを指定し、マークが検出された場合、返り値は下記のようになります。
[216.5, 448.0]
前述のサンプルコードでは、マークの中心座標を返しています。
解説
gray画像に変換されたカメラキャプチャ画像から、マーカーの座標とIDを抽出しているのが、下記の部分になります。
corners, ids, rejectedImgPoints = aruco.detectMarkers(gray, dict_aruco, parameters=parameters)
下記のコマンドで、マーカーのリストの中に、指定したIDのマーカーが含まれるかを探します。
if num_id in np.ravel(ids) :
np.whereで、マークリストidsのどのindexに、指定したマークが割り当てられているかを探します。
index = np.where(ids == num_id)[0][0]
indexを探すのは、マークリストidsと、4隅の座標リストcornersで、同じマーカーのindexは一致しているので、
これを利用して欲しいマークの座標を取得するためです。
マークの左上の座標は、下記のコマンドで抽出しています。
cornerUL = corners[index][0][0]
これは、下記の図を見ると理解できると思います。
マーカーの座標 "corners" の1つ目の引数で、indexを指定します。
ここで、indexであり、マークIDではないことに注意して下さい
マーカーの座標 "corners" の3つ目の引数で、4隅の位置指定します。
左上を0として、時計回りで数字が大きくなります。
indexを引数として渡した、 "corners" をprintで確認すると理解しやすいかと思います。
print(corners[index])
[[[164. 405.]
[208. 402.]
[206. 449.]
[163. 451.]]]
左上、右上、右下、左下の順番で座標が格納されています。
なお、今回の例では、get_mark_coordinate関数の戻り値として、
計算によって算出したマーカーの中心座標を渡しています。
スポンサーリンク
arucoを用いてマーカーIDと動画を重ねて表示する方法
次に、マーカーのIDと領域を動画に重ねて表示する方法を紹介します。
arucoモジュールを用いて、検出したIDと領域を重ねて動画として表示するコードを紹介します。
### マーカーIDと、動画を重ねて表示する
import cv2
from cv2 import aruco
import time
### --- aruco設定 --- ###
dict_aruco = aruco.Dictionary_get(aruco.DICT_4X4_50)
parameters = aruco.DetectorParameters_create()
cap = cv2.VideoCapture(0)
try:
while True:
ret, frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
corners, ids, rejectedImgPoints = aruco.detectMarkers(gray, dict_aruco, parameters=parameters)
frame_markers = aruco.drawDetectedMarkers(frame.copy(), corners, ids)
cv2.imshow('frame', frame_markers)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyWindow('frame')
cap.release()
except KeyboardInterrupt:
cv2.destroyWindow('frame')
cap.release()
出力例
マーカーIDと領域が、表示されています。
スポンサーリンク
まとめ
OpenCVのarucoモジュールの基本的な使い方をpythonのサンプルコードを用いながら紹介しました。
arucoモジュールを使うと、マーカー検出を様々なアプリに応用することができます。
使いこなせると面白いモジュールですので、是非遊びながら使い方を覚えてみてください。
スポンサーリンク