logo

ArUco 마커

실습 준비

import cv2 as cv
import numpy as np
from yeom import to_pil
dictionary = cv.aruco.getPredefinedDictionary(
    cv.aruco.DICT_6X6_250
)

기준 마커의 구조

ArUco 마커는 검은 테두리 안에 이진 코드를 넣은 정사각형 기준 마커(fiducial marker). 네 꼭짓점과 내부 코드를 함께 검출하여 마커의 ID와 방향을 구분.

  • 내부 코드는 마커 사전(dictionary)에 등록된 ID를 표현
  • 회전된 마커도 ID와 방향을 함께 식별 가능
  • 네 꼭짓점이 모두 보여야 단일 마커를 안정적으로 검출 가능
  • 카메라 교정값과 실제 마커 크기를 알면 위치와 자세 추정 가능
  • 증강 현실, 로봇 위치 추정, 카메라 교정 등에 활용

여러 ID를 가진 ArUco 마커

마커 사전과 ID

마커 사전은 같은 비트 크기를 사용하는 여러 마커 코드의 집합. 마커 생성과 검출에 같은 사전 선택 필요.

사전 내부 비트 ID 범위
cv.aruco.DICT_4X4_50 4×4 0–49
cv.aruco.DICT_5X5_100 5×5 0–99
cv.aruco.DICT_6X6_250 6×6 0–249

사전 이름의 앞 숫자는 내부 코드의 가로·세로 비트 수, 뒤 숫자는 사전에 포함된 마커 수.

마커 생성

cv.aruco.generateImageMarker()로 지정한 사전의 마커 이미지 생성.

marker_id = 23
marker_size = 400  # 출력 이미지 한 변의 픽셀 수

marker = cv.aruco.generateImageMarker(
    dictionary,
    marker_id,
    marker_size,
)

cv.imwrite("aruco-marker-23.png", marker)
marker.shape
실행 결과
(400, 400)

생성한 ID 23 ArUco 마커

marker_id는 선택한 사전의 ID 범위 안에서 선택. marker_size는 출력 이미지 한 변의 픽셀 수이며 실제 인쇄 크기와는 별개.

마커 검출

검출 장면은 aruco-scene.jpg. 앞에서 만든 ID 23 마커를 가상 카메라로 비스듬히 촬영한 것처럼 합성한 960×640 이미지.

기울어진 ID 23 마커가 놓인 검출 대상 장면

검출 이미지와 같은 사전으로 cv.aruco.ArucoDetector 생성. 코너 위치를 더 정밀하게 다듬기 위해 서브픽셀 코너 보정 사용.

image = cv.imread("aruco-scene.jpg")

parameters = cv.aruco.DetectorParameters()
# 꼭짓점을 픽셀보다 작은 단위까지 다듬어 자세 추정 정확도 향상
parameters.cornerRefinementMethod = cv.aruco.CORNER_REFINE_SUBPIX

detector = cv.aruco.ArucoDetector(
    dictionary,  # 생성할 때와 같은 사전
    parameters,
)

corners, ids, rejected = detector.detectMarkers(image)
ids
실행 결과
array([[23]], dtype=int32)
반환값 의미
corners 검출된 각 마커의 네 꼭짓점 좌표
ids corners와 같은 순서의 마커 ID
rejected 사각형 후보였지만 사전의 코드와 일치하지 않은 영역

각 마커의 꼭짓점은 왼쪽 위에서 시작하여 시계 방향으로 정렬됨. detectMarkers()는 2차원 꼭짓점과 ID를 찾으며 위치와 자세를 바로 계산하지는 않음.

검출 결과 표시

cv.aruco.drawDetectedMarkers()로 검출된 마커의 경계와 ID를 이미지에 표시.

detected = image.copy()  # 원본을 그대로 두고 사본에 표시
cv.aruco.drawDetectedMarkers(
    detected,
    corners,
    ids,
)

to_pil(detected)

ID와 네 꼭짓점을 표시한 검출 결과

검출되지 않으면 먼저 사전 일치 여부 확인. 이후 마커 크기, 초점, 조명, 흰 여백, 가림, 원근 왜곡 점검.

카메라 값과 마커 좌표 정하기

자세 추정에는 카메라 행렬 mtx, 왜곡 계수 dist, 마커의 3차원 기준점 object_points가 필요.

scene_width, scene_height = 960, 640

mtx = np.array(
    [
        [900, 0, scene_width / 2],   # 초점 거리(픽셀), 주점 x
        [0, 900, scene_height / 2],  # 초점 거리(픽셀), 주점 y
        [0, 0, 1],
    ],
    dtype=np.float64,
)
dist = np.zeros((5, 1), dtype=np.float64)  # 왜곡 없음
  • mtx: 3차원 좌표를 이미지 평면으로 옮기는 카메라 행렬. 대각선의 두 값은 초점 거리, 마지막 열은 광축이 이미지와 만나는 주점(principal point). 이 장면은 초점 거리 900픽셀의 가상 카메라로 합성하고 마커를 이미지 중앙 기준으로 배치하여 주점은 이미지 중심 (480,320)으로 설정
  • dist: 렌즈 왜곡 계수. 합성 이미지에는 렌즈 왜곡이 없으므로 모두 0으로 둠
marker_length = 0.08  # 마커 한 변의 실제 길이(m)
half = marker_length / 2

object_points = np.array(
    [
        [-half, half, 0],   # 왼쪽 위
        [half, half, 0],    # 오른쪽 위
        [half, -half, 0],   # 오른쪽 아래
        [-half, -half, 0],  # 왼쪽 아래
    ],
    dtype=np.float32,
)

object_points는 마커 자체의 좌표계에서 네 꼭짓점이 어디에 있는지를 나타냄.

  • 마커 중심을 원점으로 설정: 추정한 tvec은 마커 중심 위치
  • 마커는 z=0인 평면. x는 오른쪽, y는 위쪽 방향
  • 순서를 detectMarkers()가 반환하는 꼭짓점 순서(왼쪽 위 → 시계 방향)와 똑같이 맞춤. 순서가 어긋나면 엉뚱한 회전으로 추정됨
  • 길이를 미터로 넣었으므로 결과인 tvec의 단위도 미터가 됨

마커의 위치와 자세 추정

마커의 네 꼭짓점은 평면의 3차원 기준점과 대응. object_points, mtx, distcv.solvePnP()에 전달하여 회전 벡터와 이동 벡터 계산.

pose = detected.copy()

for marker_corners in corners:
    image_points = marker_corners.reshape(4, 2)  # (1, 4, 2)를 (4, 2)로

    success, rvec, tvec = cv.solvePnP(
        object_points,
        image_points,
        mtx,
        dist,
        flags=cv.SOLVEPNP_IPPE_SQUARE,  # 정사각 평면 마커 전용 해법
    )

    if success:
        cv.drawFrameAxes(
            pose,
            mtx,
            dist,
            rvec,
            tvec,
            marker_length / 2,  # 축의 길이는 마커 절반 크기
        )

to_pil(pose)

추정한 마커 좌표축을 표시한 결과

표시된 좌표축 읽기

cv.drawFrameAxes()는 추정한 rvectvec를 이용하여 마커의 좌표축을 이미지 위에 표시. 세 선이 만나는 지점은 마커 중심.

색상 이미지에서 확인할 방향
빨강 마커의 +x 마커의 오른쪽 방향
초록 마커의 +y 마커의 위쪽 방향
파랑 마커의 +z 마커 앞면에서 수직으로 나오는 방향

빨간 축과 초록 축은 화면에서 마커 평면의 회전과 기울기를 표현. 파란 축은 마커 앞면의 방향을 표현. 이 축들은 카메라 좌표축이 아니라 카메라에서 관찰한 마커 좌표축.

  • rvec: 좌표축을 어느 방향으로 표시할지 결정하는 마커의 회전 정보
  • tvec: 세 좌표축을 어디에서 시작할지 결정하는 마커 중심의 위치
  • tvec의 단위: marker_length에 사용한 단위와 동일

실제 마커 길이를 잘못 입력하면 추정한 거리의 크기도 같은 비율로 잘못됨. 카메라 교정이 정확하지 않으면 축이 흔들리거나 위치 오차가 커질 수 있음.

ArUco 보드

여러 마커의 상대 위치를 아는 보드는 일부 마커가 보이지 않아도 나머지 검출 결과를 이용 가능. 단일 마커보다 많은 꼭짓점을 사용해 보드 전체 자세를 더 안정적으로 추정하는 데 유리. cv.aruco.GridBoard로 격자 배치 정의.

여러 마커를 격자로 배치한 ArUco 보드

ChArUco 보드

ChArUco 보드는 체커보드와 ArUco 마커를 결합. ArUco ID로 보드 위치를 구분하고 체커보드 교차점을 정밀하게 찾을 수 있어 카메라 교정과 자세 추정에 활용. cv.aruco.CharucoBoard로 정의하고 cv.aruco.CharucoDetector로 검출.

체커보드와 ArUco 마커를 결합한 ChArUco 보드

다이아몬드 마커

다이아몬드 마커는 네 개의 ArUco 마커를 체커보드 형태로 배치. 네 마커의 ID 조합으로 다이아몬드를 식별하고 가운데 영역의 코너를 정밀하게 계산.

네 개의 ArUco 마커로 구성한 다이아몬드 마커

퀴즈

문제 1 / 8맞음: 0힌트: 0틀림: 0채점중: 0남음: 8

ArUco 마커를 생성할 때와 검출할 때 같은 사전을 사용해야 하는 이유는?

  • 사전이 마커의 비트 패턴과 ID 대응 관계를 정의하기 때문에
  • 사전이 카메라의 초점 거리를 자동으로 결정하기 때문에
  • 사전이 마커를 인쇄할 실제 크기를 지정하기 때문에
  • 사전이 출력 이미지의 너비와 높이를 고정하기 때문에

퀴즈를 풀려면 대화형 기능을 불러와야 합니다.

Previous
카메라 교정