元高専生のロボット作り

元高専生のロボット作り

主にプログラミング, 電子系について書きます。たまに機械系もやります。メモ代わりの記事ばっか書きます

pythonでArUcoライブラリを使ってARマーカの位置とか角度とか知る

こんにちは。今回はpythonです。

pythonにはARマーカ用のArUcoという機能がOpenCVの中に入っています。

ライブラリのインストール

$ python -m pip install opencv-contrib-python


anacondaでも以下でインストールできる

$ conda install -c conda-forge opencv 

早速ですが、コードは以下のようになります。
カメラ行列とかそのまま書いてあるので、すぐに実行できるようになっています。
気になる人は自分で修正してね

# -*- coding: utf-8 -*-
import cv2
import cv2.aruco as aruco
import numpy as np
from math import pi
arucoMarkerLength = 0.05

class AR():

    def __init__(self, videoPort, cameraMatrix, distortionCoefficients):
        self.cap = cv2.VideoCapture(videoPort)
        #self.cameraMatrix = np.load(cameraMatrix)
        #self.distortionCoefficients = np.load(distortionCoefficients)
        self.cameraMatrix = cameraMatrix
        self.distortionCoefficients = distortionCoefficients
        self.dictionary = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)

    def find_ARMarker(self):
        self.ret, self.frame = self.cap.read()
        if len(self.frame.shape) == 3:
            self.Height, self.Width, self.channels = self.frame.shape[:3]
        else:
            self.Height, self.Width = self.frame.shape[:2]
            self.channels = 1
        self.halfHeight = int(self.Height / 2)
        self.halfWidth = int(self.Width / 2)
        self.corners, self.ids, self.rejectedImgPoints = aruco.detectMarkers(self.frame, self.dictionary)
        #corners[id0,1,2...][][corner0,1,2,3][x,y]
        aruco.drawDetectedMarkers(self.frame, self.corners, self.ids, (0,255,0))

    def show(self):
        cv2.imshow("result", self.frame)

    def get_exist_Marker(self):
        return len(self.corners)

    def is_exist_marker(self, i):
        num = self.get_exist_Marker()
        if i >= num:
            return False
        else:
            return True

    def release(self):
        self.cap.release()

    # マーカー頂点の座標を取得
    def get_ARMarker_points(self, i):
        if self.is_exist_marker(i):
            return self.corners[i]

    def get_average_point_marker(self, i):
        if self.is_exist_marker(i):
            points = self.get_ARMarker_points(i)
            points_reshape = np.reshape(np.array(points), (4, -1))
            G = np.mean(points_reshape, axis = 0)
            cv2.circle(self.frame, (int(G[0]), int(G[1])), 10, (255, 255, 255), 5)
            return G[0], G[1]

    def get_ARMarker_pose(self, i):
        if self.is_exist_marker(i):
            rvec, tvec, _ = aruco.estimatePoseSingleMarkers(self.corners[i], arucoMarkerLength, self.cameraMatrix, self.distortionCoefficients)
            self.frame = aruco.drawAxis(self.frame, self.cameraMatrix, self.distortionCoefficients, rvec, tvec, 0.1)
            return rvec, tvec

    def get_degrees(self, i):
        if self.is_exist_marker(i):
            rvec, tvec, = self.get_ARMarker_pose(i)
            (roll_angle, pitch_angle, yaw_angle) =  rvec[0][0][0]*180/pi, rvec[0][0][1]*180/pi, rvec[0][0][2]*180/pi
            if pitch_angle < 0:
                roll_angle, pitch_angle, yaw_angle = -roll_angle, -pitch_angle, -yaw_angle
            return roll_angle, pitch_angle, yaw_angle
            
if __name__ == '__main__':

    camera_matrix = np.matrix([[538.0, 0.0, 306.0], [0.0, 538.0, 212.0], [0.0, 0.0, 1.0]])
    distortion = np.array([0.02524192, -0.15412883, -0.00789069, -0.00260461,  0.04049524])
    myCap = AR(0, camera_matrix, distortion)
    while True:
        myCap.find_ARMarker()
        myCap.get_average_point_marker(0)
        print(myCap.get_degrees(0))
        myCap.show()
        if cv2.waitKey(1) > 0:
            myCap.release()
            cv2.destroyAllWindows()
            break


以下はマーカー画像の生成コード

# -*- coding: utf-8 -*-
import cv2
import cv2.aruco as aruco

if __name__ == "__main__":
    # マーカーの辞書を取得
    dictionary = aruco.getPredefinedDictionary(aruco.DICT_4X4_50)
    # 作成するマーカーの枚数
    n = 50
    for i in range(n):
        image = aruco.drawMarker(dictionary, i, 150) # i番目のマーカ画像を作成
        fileName = str(i) + ".png"
        cv2.imwrite("img/" + fileName, image)    # マーカー画像を保存する


画像のものを作って印刷して使いました。
f:id:sgrsn1711:20180210232352p:plain

Webカメラですが、私はLogicoolのc270mをAmazonから購入しました。
今では価格が上がっていますね。まあ安心感はあるでしょうけど


他のだと、webカメラは今のところこれが安くてよさげ



実行してARマーカーをかざしてやれば以下のようにロール、ピッチ、ヨーの順に角度が出力されます。
f:id:sgrsn1711:20180210232917p:plain


追記(2018.3.8)
ARマーカーを正面から撮影するとy軸の正負がどっちにあるか判断できないっぽくて、角度の符号がおかしくなってしまいました。
そのため、AR::getAngle()関数に

if pitch_angle < 0:
    roll_angle, pitch_angle, yaw_angle = -roll_angle, -pitch_angle, -yaw_angle

と追記しました。
もっといい方法ありませんか。。。?