我目前正在学习openCV的"aruco"模块,特别关注ArUco标记和AprilTags的poseEstimation。
查看子像素精度时,我遇到了一个奇怪的行为,如以下代码所示:如果我提供了"完美"校准(例如,cx/cy等于图像中心并且失真被设置为零)和具有已知边缘长度的"完美"标记cv. detectMarkers将仅在旋转为0/90/180或270度时产生正确的值。对于其它取向为(几乎)恒定值,很清楚,在0/90/180/270度的特定Angular 处,角落中的像素产生尖锐的转变,因此可以高精度地检测。然而,我很难看出在所有其他情况下低估的长度是从哪里来的。这是一个bug还是由一些三角函数引起的?--〉看看下面的脚本生成的图形:姿态的误差是角点检测的误差造成的,因此检测精度将取决于码的取向。
我还检查了Aruco标记和不同的亚像素化方法。"峰值"仍然存在,尽管其间的Angular 行为会发生变化。
我很确定,这不是由于与标记旋转相关的插值,因为我也可以在真实数据中观察到相同的行为(但请注意,峰值的"高度"似乎在某种程度上取决于插值方法。您可以通过更改cv. warpAffine中的标志来测试这一点,例如更改为cv. INTER_LINEAR)。
我的问题是:
1.峰值是由于漏洞还是预期行为?
1.如果是后者,你能帮我弄明白为什么吗?
1.是否有办法消除这种精度的方向依赖性(除了增加系统分辨率,从而无需子像素化)?
编辑:注意AprilTag函数最近才被添加到openCV中,所以你需要升级到最新版本,这在一些标准仓库中还没有。你可以在conda-forge上获得最新版本。/编辑
# -*- coding: utf-8 -*-
import numpy as np
import cv2 as cv
import pylab as plt
""" generate an "ideal" calibration with zero distortion and perfect alignment
of the main optical axis: """
cam_matrix = np.array([[1.0e+04, 0.00000000e+00, 1.22400000e+03],
[0.00000000e+00, 1.0e+04, 1.02400000e+03],
[0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])
dist_coeffs = np.array([[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.],
[0.]])
# define detection parameters
marker_length = 30.00 # some arbitrary value
marker_length_px = 700
marker_id = 3
dictionary = cv.aruco.getPredefinedDictionary(cv.aruco.DICT_APRILTAG_16H5)
para = cv.aruco.DetectorParameters_create()
para.cornerRefinementMethod = cv.aruco.CORNER_REFINE_APRILTAG
para.aprilTagDeglitch = 0
para.aprilTagMinWhiteBlackDiff = 30
para.aprilTagMaxLineFitMse = 20
para.aprilTagCriticalRad = 0.1745329201221466 *6
para.aprilTagMinClusterPixels = 5
para.maxErroneousBitsInBorderRate = 0.35
para.errorCorrectionRate = 1.0
para.minMarkerPerimeterRate = 0.05
para.maxMarkerPerimeterRate = 4
para.polygonalApproxAccuracyRate = 0.05
para.minCornerDistanceRate = 0.05
marker_length_list = []
tvec_z_list = []
# generate pictures (AprilTag ID: 3 centered in image will be rotated by fixed angular steps, e. g. 10 degrees)
degrees_list = np.linspace(0,350,36, dtype=np.int).tolist()
marker = cv.aruco.drawMarker(dictionary, marker_id, marker_length_px)
img = np.zeros((2048, 2448), np.uint8)+255
img[674:1374, 874:1574] = marker
cv.imshow("Original", img)
cv.imwrite("original.png", img)
rows, cols = img.shape
for entry in degrees_list:
# rotate original picture
rot_mat = cv.getRotationMatrix2D((((rows-1)/2),(cols-1)/2), entry, 1)
rot_img = cv.warpAffine(img, rot_mat, (cols, rows), flags=cv.INTER_CUBIC) # interpolation changes the "peak amplitude" e.g. try cv.INTER_LINEAR instead
# detect marker and get pose estimate
corners, ids, rejected = cv.aruco.detectMarkers(rot_img,dictionary,parameters=para)
my_index = ids.tolist().index([marker_id])
fCorners = corners[my_index]
fRvec,fTvec, _obj_points = cv.aruco.estimatePoseSingleMarkers(fCorners, marker_length, cam_matrix, dist_coeffs)
# calculate the respective edge length for each side
L1 = abs(np.sqrt(np.square(fCorners[0][0][0]-fCorners[0][1][0])+np.square(fCorners[0][0][1]-fCorners[0][1][1])))
L2 = abs(np.sqrt(np.square(fCorners[0][0][0]-fCorners[0][3][0])+np.square(fCorners[0][0][1]-fCorners[0][3][1])))
L3 = abs(np.sqrt(np.square(fCorners[0][2][0]-fCorners[0][1][0])+np.square(fCorners[0][2][1]-fCorners[0][1][1])))
L4 = abs(np.sqrt(np.square(fCorners[0][2][0]-fCorners[0][3][0])+np.square(fCorners[0][2][1]-fCorners[0][3][1])))
mean_length = (L1+L2+L3+L4)/4
# append results
marker_length_list. append(mean_length)
tvec_z_list.append(fTvec[0][0][2])
plt.figure("TVEC Z")
plt.plot(degrees_list, tvec_z_list, "go--")
plt.xlabel("marker rotation angle (°)")
plt.ylabel("TVEC Z (units of length)")
plt.figure("Mean marker length (should be 700)")
plt.plot(degrees_list, marker_length_list, "bo--")
plt.xlabel("marker rotation angle (°)")
plt.ylabel("marker length (pixels)")
编辑2:正如Christoph Rackwitz所建议的,下面是脚本生成的图片输出:
标记长度:理想值应为700:
标记距离:应该是旋转不变的:
1条答案
按热度按时间8hhllhi21#
Christoph Rackwitz的回答(参见评论):
您只会得到像素差异的2%(长度为30 ppm,Z轴为30 ppm)。warpAffine使用定点数学,5个小数位。三次插值。另外:角点细化方法(希望能在边缘上工作,而不是角点)......这一切都发生在"线性色彩空间"(变形)中。如果你给这些真实的图片,这些是gammaMap的,网络摄像头应用锐化过滤器,所以你会得到更疯狂的结果