Python 详细了解openCV aruco标记检测/姿势估计:亚像素精度

Python 详细了解openCV aruco标记检测/姿势估计:亚像素精度,python,opencv,aruco,pose-estimation,apriltags,Python,Opencv,Aruco,Pose Estimation,Apriltags,我目前正在学习openCV的“aruco”模块,特别关注aruco标记和AprilTags的位置估计 查看亚像素精度,我遇到了一个奇怪的行为,下面的代码演示了这一点: 如果我确实提供了“完美”校准(例如cx/cy等于图像中心,失真设置为零)和具有已知边缘长度的“完美”标记,则cv.detectMarkers仅在旋转为0/90/180或270度时才会产生正确的值。亚像素例程为其他方向生成(几乎)常量值,但处于“移位”级别。很明显,在0/90/180/270度的特定角度下,角点中的像素产生了急剧的转

我目前正在学习openCV的“aruco”模块,特别关注aruco标记和AprilTags的位置估计

查看亚像素精度,我遇到了一个奇怪的行为,下面的代码演示了这一点: 如果我确实提供了“完美”校准(例如cx/cy等于图像中心,失真设置为零)和具有已知边缘长度的“完美”标记,则cv.detectMarkers仅在旋转为0/90/180或270度时才会产生正确的值。亚像素例程为其他方向生成(几乎)常量值,但处于“移位”级别。很明显,在0/90/180/270度的特定角度下,角点中的像素产生了急剧的转变,因此可以高精度地检测。然而,我很难看出在所有其他情况下被低估的长度是从何而来的。这是一个错误还是由三角函数引起的? -->看看下面的脚本生成的图形:姿势中的错误是由角点检测中的错误导致的。因此,检测精度将取决于代码的方向

我还检查了阿鲁科标记和不同的亚像素方法。“峰值”仍然存在,尽管其间的角度行为将发生变化

我很确定,这不是由于与标记旋转相关的插值,因为我也可以在实际数据中观察到相同的行为(但请注意,峰值的“高度”似乎在某种程度上取决于插值方法。您可以通过将cv.warpAffine中的标志更改为cv.INTER_LINEAR来测试这一点)

我的问题是:

  • 峰值是由bug引起的还是预期的行为
  • 如果是后者,你能帮我理解为什么吗
  • 有没有办法消除这种精度的方向依赖性(除了增加系统的分辨率,这样就不需要亚像素)
  • 编辑:请注意,AprilTag函数最近才添加到openCV中,因此您需要升级到一些标准存储库中尚未提供的最新版本。你可以用e。G在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 an 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)")