什么是 Project Tango 镜头畸变模型?

What is the Project Tango lens distortion model?

Project Tango C API documentation 表示 TANGO_CALIBRATION_POLYNOMIAL_3_PARAMETERS 镜头畸变建模为:

x_corr_px = x_px (1 + k1 * r2 + k2 * r4 + k3 * r6) y_corr_px = y_px (1 + k1 * r2 + k2 * r4 + k3 * r6)

未失真坐标是失真坐标的幂级数函数。 Java API 中还有另一个定义,但该描述不够详细,无法判断函数映射的方向。

我在正确注册时遇到了很多麻烦,我怀疑映射实际上可能会朝相反的方向发展,即 扭曲的 坐标是一种力量未失真 坐标系列。如果相机校准是使用 OpenCV 生成的,那么问题的原因可能是 OpenCV 文档自相矛盾。最容易找到和理解的描述是 OpenCV camera calibration tutorial,它与 Project Tango 文档一致:

但另一方面,OpenCV API documentation 指定映射是相反的:

我对 OpenCV 的实验表明它的 API 文档看起来是正确的,而教程是错误的。正 k1(所有其他失真参数设置为零)表示枕形失真,负 k1 表示桶形失真。对于这种不匹配,这与 Wikipedia says about the Brown-Conrady model and will be opposite from the Tsai model. Note that distortion can be modeled either way depending on what makes the math more convenient. I opened a bug 与 OpenCV 相匹配。

所以我的问题是:Project Tango 镜头畸变模型是否与 OpenCV 中实现的模型相同(尽管有文档)?

这是我用彩色相机拍摄的图像(可以看到轻微的枕形):

这是 Tango 服务报告的相机校准:

distortion = {double[5]@3402}
[0] = 0.23019999265670776
[1] = -0.6723999977111816
[2] = 0.6520439982414246
[3] = 0.0
[4] = 0.0
calibrationType = 3
cx = 638.603
cy = 354.906
fx = 1043.08
fy = 1043.1
cameraId = 0
height = 720
width = 1280

以下是在 python 中使用 OpenCV 消除失真的方法:

>>> import cv2
>>> src = cv2.imread('tango00042.png')
>>> d = numpy.array([0.2302, -0.6724, 0, 0, 0.652044])
>>> m = numpy.array([[1043.08, 0, 638.603], [0, 1043.1, 354.906], [0, 0, 1]])
>>> h,w = src.shape[:2]
>>> mDst, roi = cv2.getOptimalNewCameraMatrix(m, d, (w,h), 1, (w,h))
>>> dst = cv2.undistort(src, m, d, None, mDst)
>>> cv2.imwrite('foo.png', dst)

这就产生了这个,它在顶部边缘可能有点矫枉过正,但比我对反向模型的尝试要好得多:

Tango C-API Docs 表示 (x_corr_px, y_corr_px)"corrected output position"。这个校正后的输出位置需要按焦距缩放并按投影中心偏移以对应于扭曲的像素坐标。

因此,要将一个点投影到图像上,您必须:

  1. 变换 3D 点,使其位于相机的框架内
  2. 将点转换为归一化图像坐标(x, y)
  3. 计算归一化图像坐标的 r2、r4、r6 (r2 = x*x + y*y)
  4. 根据上述等式计算(x_corr_px, y_corr_px)

    x_corr_px = x (1 + k1 * r2 + k2 * r4 + k3 * r6)
    y_corr_px = y (1 + k1 * r2 + k2 * r4 + k3 * r6)
    
  5. 计算扭曲坐标

    x_dist_px = x_corr_px * fx + cx
    y_dist_px = y_corr_px * fy + cy
    
  6. 在原始的变形图像缓冲区上绘制 (x_dist_px, y_dist_px)

这也意味着 corrected 坐标是 normalized 坐标,由标准化图像坐标大小的幂级数缩放。 (这与问题所暗示的相反)

查看 OpenCV 中 cvProjectPoints2 的实现 (see [opencv]/modules/calib3d/src/calibration.cpp),OpenCV 中的 "Poly3" 失真与 Tango 中的应用方向相同。所有 3 个版本(Tango Docs、OpenCV 教程、OpenCV API)都是一致且正确的。

祝你好运,希望这对您有所帮助!

(更新:仔细看一下代码,看起来校正后的坐标和扭曲的坐标不一样。我已经删除了我的回复中不正确的部分,剩下的这个答案的部分内容仍然正确。)

也许这不是 post 的正确位置,但我真的很想分享 OpenCV 中使用的代码的可读版本,以实际 纠正 失真。

我敢肯定,我不是唯一一个需要 x_correctedy_corrected 但找不到简单易懂的公式的人。

我在 Python 中重写了 cv2.undistortPoints 的重要部分,您可能会注意到修正是 迭代地 执行的。这很重要,因为 9 次方多项式的解不存在,我们所能做的就是多次应用它的反向版本来得到数值解。

def myUndistortPoint((x0, y0), CM, DC):
    [[k1, k2, p1, p2, k3, k4, k5, k6]] = DC
    fx, _, cx = CM[0]
    _, fy, cy = CM[1]

    x = x_src = (x0 - cx) / fx
    y = y_src = (y0 - cy) / fy

    for _ in range(5):
        r2 = x**2 + y**2
        r4 = r2**2
        r6 = r2 * r4

        rad_dist = (1 + k4*r2 + k5*r4 + k6*r6) / (1 + k1*r2 + k2*r4 + k3*r6)
        tang_dist_x = 2*p1 * x*y + p2*(r2 + 2*x**2)
        tang_dist_y = 2*p2 * x*y + p1*(r2 + 2*y**2)

        x = (x_src - tang_dist_x) * rad_dist
        y = (y_src - tang_dist_y) * rad_dist

    x = x * fx + cx
    y = y * fy + cy

    return x, y

要加快速度,您只能使用三次迭代,在大多数相机上,这将提供足够的精度来适应像素。