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
마커 사전은 같은 비트 크기를 사용하는 여러 마커 코드의 집합. 마커 생성과 검출에 같은 사전 선택 필요.
| 사전 | 내부 비트 | ID 범위 |
|---|---|---|
cv.aruco.DICT_4X4_50 | 0–49 | |
cv.aruco.DICT_5X5_100 | 0–99 | |
cv.aruco.DICT_6X6_250 | 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)

marker_id는 선택한 사전의 ID 범위 안에서 선택. marker_size는 출력 이미지 한 변의 픽셀 수이며 실제 인쇄 크기와는 별개.
마커 주변의 흰 여백 유지
검은 테두리가 배경과 구분되도록 인쇄하거나 화면에 표시할 때 마커 주변에 충분한 흰 여백을 둠. 마커를 비정상적으로 늘이거나 줄여 종횡비를 바꾸지 않음.
마커 검출
검출 장면은 aruco-scene.jpg. 앞에서 만든 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)

검출되지 않으면 먼저 사전 일치 여부 확인. 이후 마커 크기, 초점, 조명, 흰 여백, 가림, 원근 왜곡 점검.
카메라 값과 마커 좌표 정하기
자세 추정에는 카메라 행렬 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픽셀의 가상 카메라로 합성하고 마커를 이미지 중앙 기준으로 배치하여 주점은 이미지 중심 으로 설정dist: 렌즈 왜곡 계수. 합성 이미지에는 렌즈 왜곡이 없으므로 모두0으로 둠
실제 촬영 이미지에서는 교정값 사용
여기서는 합성 장면이므로 두 값을 직접 지정 가능. 실제 촬영 사진은 카메라 교정으로 구한 mtx와 dist를 그대로 사용. 값이 부정확하면 축이 흔들리거나 거리 오차가 커짐.
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은 마커 중심 위치 - 마커는 인 평면. 는 오른쪽, 는 위쪽 방향
- 순서를
detectMarkers()가 반환하는 꼭짓점 순서(왼쪽 위 → 시계 방향)와 똑같이 맞춤. 순서가 어긋나면 엉뚱한 회전으로 추정됨 - 길이를 미터로 넣었으므로 결과인
tvec의 단위도 미터가 됨
마커의 위치와 자세 추정
마커의 네 꼭짓점은 평면의 3차원 기준점과 대응. object_points, mtx, dist를 cv.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()는 추정한 rvec와 tvec를 이용하여 마커의 좌표축을 이미지 위에 표시. 세 선이 만나는 지점은 마커 중심.
| 색상 | 축 | 이미지에서 확인할 방향 |
|---|---|---|
| 빨강 | 마커의 축 | 마커의 오른쪽 방향 |
| 초록 | 마커의 축 | 마커의 위쪽 방향 |
| 파랑 | 마커의 축 | 마커 앞면에서 수직으로 나오는 방향 |
빨간 축과 초록 축은 화면에서 마커 평면의 회전과 기울기를 표현. 파란 축은 마커 앞면의 방향을 표현. 이 축들은 카메라 좌표축이 아니라 카메라에서 관찰한 마커 좌표축.
파란 축이 짧게 보이는 경우
마커가 카메라를 정확히 정면으로 바라보면 파란 축이 카메라의 시선 방향과 거의 겹침. 깊이 방향의 선을 2차원 이미지에 투영하므로 파란 선이 매우 짧거나 점처럼 보일 수 있음.
rvec: 좌표축을 어느 방향으로 표시할지 결정하는 마커의 회전 정보tvec: 세 좌표축을 어디에서 시작할지 결정하는 마커 중심의 위치tvec의 단위:marker_length에 사용한 단위와 동일
실제 마커 길이를 잘못 입력하면 추정한 거리의 크기도 같은 비율로 잘못됨. 카메라 교정이 정확하지 않으면 축이 흔들리거나 위치 오차가 커질 수 있음.
ArUco 보드
여러 마커의 상대 위치를 아는 보드는 일부 마커가 보이지 않아도 나머지 검출 결과를 이용 가능. 단일 마커보다 많은 꼭짓점을 사용해 보드 전체 자세를 더 안정적으로 추정하는 데 유리. cv.aruco.GridBoard로 격자 배치 정의.

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

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