1. ChArUco 介绍(Detection of ChArUco Corners)
Chessboard
具有高的交点精度,但是交点提取比较困难。ArUco
能够快速检测,但即使使用亚像素精度提取,提取的交点精度也不甚理想。ChArUco
集成了Chessboard
的高精度与ArUco
易用性的优点。
使用
ArUco
的特征插值出棋盘格黑白块的内角点
2. ChArUco 创建
charuco_board = cv.aruco.CharucoBoard_create(squaresX, squaresY, squareLength, markerLength, dictionary )
2.1 算子参数
- squaresX
X方向棋盘格黑白块的数量 - squaresY
Y方向棋盘格黑白块的数量 - squareLength
棋盘格黑白块的边长,通常以米
为单位 - markerLength
标志点的边长,单位与参数squareLength
的单位相同 - dictionary
标志块的编码集合。OpenCV提供了一些内置编码。内置编码命名规则(方便记忆):
DICT_4X4_50
- 4X4 两个
4
分别表示标志块在X与Y方向被划分的数量 - 50 表示该集合种包含的标志块图案的数量
- 4X4 两个
2.2 参数示意
- squaresX,squaresY,squareLength,markerLength
- dictionary
- cv.aruco.DICT_4X4_50
- cv.aruco.DICT_7X7_50
- cv.aruco.DICT_4X4_50
- 编码图案的最小模块(以
cv.aruco.DICT_4X4_50
为例)
默认情况下编码区域
到标志块区域
有一个模块单位
的距离。这个距离可通过draw
函数的borderBits
参数进行修改。当指定的编码区域为4X4
时,实际的标志块区域被6X6
等分。边缘有一个模块单位的间距。
2.3 算子返回值
- charuco_board
表示一个CharucoBoard
对象实例
3. ChArUco 绘制
img = cv.aruco_CharucoBoard.draw(charuco_board, outSize, marginSize = 1, borderBits = 1)
aruco_CharucoBoard.draw(self, outSize, img=None, marginSize=None, borderBits=None)
3.1 算子参数
- charuco_board
CharucoBoard
对象实例 - outSize
输出图像的像素尺寸,以元组
的形式输入,格式(image_width, image_height)
- marginSize
>=0
棋盘格黑白块距离图像边界的最小像素宽度 - borderBits
>=1
标志块种的图案编码区域距离标志块边缘的距离,以编码图案的最小模块为单位
3.2 参数示意
- marginSize
- = 1
- = 10
- = 1
- borderBits
- = 1
- = 3
- = 1
3.3 算子返回值
- img
指定尺寸的二值图像
4. ChArUco 创建与绘制样例
# 如果提示找不到cv2.aruco,请执行以下语句(重装python)
# pip uninstall opencv-python
# pip uninstall opencv-contrib-python
# pip install opencv-python
# pip install opencv-contrib-python
import cv2 as cv
dictionary = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_4X4_50)
board=cv.aruco.CharucoBoard_create(7, 7, 0.04, 0.02, dictionary)
img = cv.aruco_CharucoBoard.draw(board, (600, 600), marginSize = 10, borderBits=1)
cv.imwrite('charuco.png',img)
corners, ids, rejectedImgPoints = cv.aruco.detectMarkers(img, dictionary)
print(corners)
print(ids)
print(rejectedImgPoints)
if(len(ids) > 0):
cv.aruco.estimatePoseSingleMarkers()
5. ChArUco 标志块检测
当执行ChArUco
检测时,首先检测出标志块的角点,每个标志块包含四个角点,按照顺时针顺序排列。
detectMarkers(image, dictionary, corners=None, ids=None, parameters=None, rejectedImgPoints=None, cameraMatrix=None, distCoeff=None)
5.1 算子参数
-
image
输入图像 -
dictionary
Charuco
的类型 -
corners
标志块的角点在输入图像坐标系种的坐标。对于每个检测到的标志块会输出四个角点,按照顺时针排列。第一个角点为标志块的左上角点。标志块在corners
中的顺序为右下角开始,先从右到左,然后从下到上- ids
检测到的标志块的ID
,从零开始。第一个标志块位于生成的标定板图像的左上角。从左到右,从上到下 -
parameters
标志块的检测参数 -
rejectedImgPoints
指示对应位置的标志块的内部编码是不正确的(无法被识别),用于调试目的 -
cameraMatrix
相机的内参矩阵(float) [ f x 0 c x 0 f y c y 0 0 1 ] \left[ \begin{matrix} &{f_x}&{0}&{c_x}\\&{0}&{f_y}&{c_y}\\&{0}&{0}&{1}\end{matrix} \right] ⎣⎡fx000fy0cxcy1⎦⎤ -
distCoeff
相机的扭曲系数 ( k 1 , k 2 , p 1 , p 2 [ , k 3 [ , k 4 , k 5 , k 6 ] , [ s 1 , s 2 , s 3 , s 4 ] ] ) (k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6],[s_1, s_2, s_3, s_4]]) (k1,k2,p1,p2[,k3[,k4,k5,k6],[s1,s2,s3,s4]])
6. ChArUco 标志块绘制
drawDetectedMarkers(image, corners, ids=None, borderColor=None)
image
绘制标志块的图像,必须为1/3通道的图像
corners
相对于输入图像坐标系的标志块角点坐标
ids
标志块的
ID
borderColor
标志块绘制的颜色
7. ChArUco 棋盘格内角点坐标
棋盘格内角点的坐标是通过标志块角点坐标插值得到。
retval, charucoCorners, charucoIds = interpolateCornersCharuco(markerCorners, markerIds, image, board, cameraMatrix=None, distCoeffs=None, minMarkers=None)
markerCorners
标志块的角点坐标markerIds
标志块的ID
image
用于角点精定位board
标定板类型charucoCorners
输出,棋盘格内角点的坐标charucoIds
输出,棋盘格内焦点的ID
cameraMatrix
相机内参矩阵distCoeffs
相机扭曲系数minMarkers
相邻的标志块被检测出的最小数量retval
被插值出的内角点数量
8. 标定板位姿估计
retval, rvec, tvec = estimatePoseCharucoBoard(charucoCorners, charucoIds, board, cameraMatrix, distCoeffs, rvec, tvec[, useExtrinsicGuess]) ```
9. 源码
import cv2 as cv
import numpy as np
# 创建ChArUco标定板
dictionary = cv.aruco.getPredefinedDictionary(dict=cv.aruco.DICT_6X6_250)
board = cv.aruco.CharucoBoard_create(squaresY=7,
squaresX=5,
squareLength=0.04,
markerLength=0.02,
dictionary=dictionary)
img_board = board.draw(outSize=(600, 500), marginSize=10, borderBits=1)
cv.imwrite(filename='charuco.png', img=img_board, params=None)
camera_matrix = np.array([[532.79536562, 0, 342.4582516],
[0, 532.91928338, 233.90060514],
[0, 0, 1.]])
dist_coefs = np.array([-2.81086258e-01, 2.72581018e-02, 1.21665908e-03, -1.34204275e-04, 1.58514022e-01])
# 主要用于图形的绘制与显示
img_color = cv.cvtColor(src=img_board,
code=cv.COLOR_GRAY2BGR,
dstCn=None)
# 查找标志块的左上角点
corners, ids, rejectedImgPoints = cv.aruco.detectMarkers(image=img_board,
dictionary=dictionary,
parameters=None,
cameraMatrix=camera_matrix,
distCoeff=dist_coefs)
# print(corners)
# print(ids)
if len(ids) > 0:
print((int(corners[0][0][0][0]), int(corners[0][0][0][1])))
print((int(corners[1][0][0][0]), int(corners[1][0][0][1])))
cv.circle(img_color, (int(corners[0][0][0][0]), int(corners[0][0][0][1])), 8, [0, 255, 0])
cv.circle(img_color, (int(corners[1][0][0][0]), int(corners[1][0][0][1])), 8, [0, 255, 0])
cv.circle(img_color, (int(corners[2][0][0][0]), int(corners[2][0][0][1])), 8, [0, 255, 0])
# 绘制标志块的左上角点与对应的ID
cv.aruco.drawDetectedMarkers(image=img_color, corners=corners, ids=ids, borderColor=None)
cv.imshow("out", img_color)
cv.waitKey()
# 棋盘格黑白块内角点
retval, charucoCorners, charucoIds = cv.aruco.interpolateCornersCharuco(markerCorners=corners,
markerIds=ids,
image=img_board,
board=board,
cameraMatrix=camera_matrix,
distCoeffs=dist_coefs)
print(charucoCorners)
print(charucoIds)
if len(charucoIds) > 0:
# 绘制棋盘格黑白块内角点
cv.aruco.drawDetectedCornersCharuco(img_color, charucoCorners, charucoIds, [0, 0, 255])
cv.imshow("out", img_color)
cv.waitKey()
rvec = None
tvec = None
retval, rvec, tvec = cv.aruco.estimatePoseCharucoBoard(charucoCorners, charucoIds, board, camera_matrix,
dist_coefs, rvec, tvec)
if retval:
cv.aruco.drawAxis(img_color, camera_matrix, dist_coefs, rvec, tvec, 0.01)
cv.imshow("out", img_color)
cv.waitKey()