关于相机内参与外参的浅读

这篇具有很好参考价值的文章主要介绍了关于相机内参与外参的浅读。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

原文地址:https://tunmx.github.io/posts/CH-Camera-internal-and-external-parameters/

学习人脸3D重建的第一天,在首次接触3D相关的内容,必须要搞清楚相机的成像原理,如何将真实三维空间中的三维点与显示器、屏幕和图像等二维成像的平面映射,以及了解该过程的推导方式和相关坐标系的换算,如像素坐标,图像坐标,相机坐标以及世界坐标这四种关系的变换。

主要内容从以下博主的文章整理,并结合自己的实验代码进行测试,推荐直接看原帖,无中间商赚差价:

https://www.cnblogs.com/wangguchangqing/p/8126333.html#autoid-0-5-0

一文带你搞懂相机内参外参(Intrinsics & Extrinsics) - Yanjie Ze的文章 - 知乎 

针孔模型

从图中所示,我们可以清楚的看到两种坐标系:

  • 相机坐标系(3D):以光心中心点相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档为原点,建立相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档三维坐标系;

  • 图像坐标系(2D):以被投射的平面中相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档为原点,建立相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档二维坐标系。

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

从图所示,取真实世界中的任意一点相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档通过相机的光心相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档点映射到成像平面上的点相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档,其中我们令点相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档,则对应到点相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档,这边比较特殊,将成像的平面与光点的距离记为f,即为像距,所以可以用以下图表示坐标系和映射点之间的关系:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

通过上图相似三角形关系可以得出以下关系式:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

其中出现负号的原因是因为坐标轴在映射过程中的成像为倒立所导致,为了表达方便,看到一个博主是这样解释和处理的:

为了表示起来更方便,我们把成像平面从相机的后面对称到前面去,这样,负号就没有了。

一文带你搞懂相机内参外参(Intrinsics & Extrinsics) - Yanjie Ze的文章 - 知乎 

经过转换后的关系:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

通现在我们把上面的关系式以解出相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档点为目的进行变形,可得:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

上面便是整理好的小孔模型基本公式,通过这些公式我们可以进一步的去推算利用该模型下求解相机的内参和外参。

简单的描述一下相机内参:相机内参描述了相机本身自带的一些属性,如焦距、像素间距等;通常是用一个内参矩阵K来表示,这个矩阵K用于描述从三维场景到二维场景的映射形状和大小。

上一步我们求解出了小孔模型的基本公式,需要进一步将所述的坐标点映射到像素坐标系中,像素坐标定义通常如下:

  • 像素坐标系(2D):通常以图像的左上角为坐标的原点,u轴从左到右与x轴平行,v轴从上到下与y轴平行;

我们设像素坐标轴同时在u轴和v轴缩放了S的倍数,倍数定义为相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档,即u轴缩放了α倍,v轴缩放了β倍;同时,原点坐标也平移了C个像素点,即相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档,在与上一步求解的点相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档的坐标关系如下:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

将上一步以相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档关系得出的小孔模型公式代入可得:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

我们令相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档,可得:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

我们将方程组写成齐次坐标的形式:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

我们可以把z挪到左边得到:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

并且,令相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

可得简化的表达式:相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

通过上面的推导,我们已经计算出了相机内参(Camera Intrinsics)内参矩阵K,通常在实际项目中该内参的得出方式通常需要标定,标定的方式后面等到具体实践再进行整理。

相机外参

简单的描述一下相机的外参:相机的外参主要描述的是相机在三维场景下的位置以及镜头朝向,通常以一个旋转矩阵R平移向量t进行表示,描述了相机的位置、方向和观察角度,决定了相机从哪个角度观察场景。

在上面的推导过程中,我们已经求出了坐标系之间的关系以及内参矩阵,其中内参矩阵是通过相机坐标系与像素坐标之间的关系得出,所以我们这步需要通过世界坐标系与相机坐标系之间的关系来推导相机外参,并记录过程。

根据上面描述的内容,我们继续以上述为例,设相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档是在相机坐标系的点,相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档是在世界坐标系下的点,我们可以使用一个旋转矩阵相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档和一个平移向量相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档,把相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档变换到相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档,即:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

其中,相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档是一个3x3的旋转矩阵,相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档是一个3x1的平移向量,我们将其使用其次坐标表达:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

可以改变式子,把加号也干掉:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

所以将旋转矩阵相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档和平移向量相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档带入到上述公式可得:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

所以我们可以使用该矩阵来表示相机的外参:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

内参与外参组合使用

由于上述内容可得知:

相机外参公式:相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

相机内参公式:相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

则将外参公式带入内参可得如下结果:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

代码示例

完成上述公式的推导后,使用Python环境下进行实际操作一下,仅使用numpy的ndarray作为数据结构和opencv的绘图工具以及solvePnP来验证相机外参解法。

首先我们需要先定义一个被观测的目标,并将其定义在世界坐标中;这里我们就选择使用一个立方体作为观测目标:


import cv2
import numpy as np

# 定义方形画布像素坐标长度
canvas_square_size = 320
# 定义立方体的边长
length = 1

# 定义立方体的8个顶点坐标 使用世界坐标作为表达
vertices_w = np.array([
    [-length / 2, -length / 2, -length / 2],
    [-length / 2, -length / 2, length / 2],
    [-length / 2, length / 2, -length / 2],
    [-length / 2, length / 2, length / 2],
    [length / 2, -length / 2, -length / 2],
    [length / 2, -length / 2, length / 2],
    [length / 2, length / 2, -length / 2],
    [length / 2, length / 2, length / 2]])
print("像素坐标系顶点集合: ", vertices_uv.shape)
# 打印结果:
# 世界坐标系顶点集合:  (8, 3)

定义好世界坐标下的立方体后,我们来手动定义一组相机外参,上述提到过,相机外参是由一个旋转矩阵R和一个平移向量t组成,这里我们利用旋转矩阵的特性,定义一个携带让其沿着roll轴旋转一定角度的RulerAngle属性的旋转矩阵R_roll,并手动设置一个t向量;使用世界坐标系对其进行变换得出相机坐标系顶点集,即使用公式:相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档,求解步骤代码如下:


# 定义一个角度
a = 45
# 转换为弧度制
a = np.deg2rad(a)

# 手动定一个相机外参R旋转矩阵,并设置让其绕roll轴旋转a度
R_roll = np.array([
    [1, 0, 0],
    [0, np.cos(a), -np.sin(a)],
    [0, np.sin(a), np.cos(a)]
])
# 手动定一个相机外参的偏移向量t 即在x, y, z的位置看面朝单位
t1 = 0
t2 = 0
t3 = 5  # 数值越大则距离观测目标距离则越长
T = np.array([t1, t2, t3])

# 求基于相机坐标系的顶点集
vertices_c = np.matmul(R_roll, vertices_w.T).T + T

再求出新的点集后,我们手动定义一组相机内参的K矩阵,并将中心点设置在像素坐标系中画布的中心点,以便我们可视化时可能更清晰的观测到目标;定义内参K矩阵后,我们定义一个透视投影函数,用于将三维的坐标系投影到像素坐标系中,即公式:相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档,函数定义如下:


def perspective_projection(vertices, K):
    """use perspective projection"""
    vertices_2d = np.matmul(K, vertices.T).T
    vertices_2d[:, 0] /= vertices_2d[:, 2]
    vertices_2d[:, 1] /= vertices_2d[:, 2]
    vertices_2d = vertices_2d[:, :2].astype(np.int32)

    return vertices_2d

定义好函数perspective_projection后,我们在主程序中继续定义我们的内参矩阵K,并使用函数进行坐标转换:


# 手动定一组相机内参K
fx = 800
fy = 800
cx = canvas_square_size // 2
cy = canvas_square_size // 2
K = np.array([
    [fx, 0, cx],
    [0, fy, cy],
    [0, 0, 1]
])

# 使用透视投影解出像素坐标的顶点集
vertices_uv = perspective_projection(vertices_c, K)
print("像素坐标系顶点集合: ", vertices_uv.shape)
# 打印结果:
# 世界坐标系顶点集合:  (8, 3)

完成像素坐标系的转换后,我们准备一个绘制函数,用来显示我们的处理结果,将投影后的像素坐标系显示出来:


def display_vertices_uv(vertices_2d, win_name='vertices', wait_key=0, canvas_size=(320, 320)):
    """Show the vertices on uv-coordinates"""
    img = np.zeros((canvas_size[0], canvas_size[1], 3), dtype=np.uint8)
    edges = np.array([
        [0, 1], [0, 2], [0, 4],
        [1, 3], [1, 5],
        [2, 3], [2, 6],
        [4, 5], [4, 6],
        [7, 5], [7, 6], [7, 3]])

    for edge in edges:
        pt1 = tuple(vertices_2d[edge[0]])
        pt2 = tuple(vertices_2d[edge[1]])
        cv2.line(img, pt1, pt2, (0, 255, 255), 2)

    cv2.imshow(win_name, img)
    cv2.waitKey(wait_key)

定义好函数后,我们回到主程序运行代码显示结果:


# 显示求解后的uv顶点集
display_vertices_uv(vertices_uv, canvas_size=(canvas_square_size, canvas_square_size))

结果如下:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

可以看到我们已经成功的使用自己定义的模拟相机内外参去对该立方体进行世界坐标转换到像素坐标,并成功可视化观测到这个立方体。

这里我们将相机通过外参矩阵R的形式设置的是把相机摆放在一个以roll转角45度的观测角度,为了以更加丰富的角度观测到这个立方体,我们可以在roll转角45度的情况下,再加入另外一种欧拉角属性pitch,使其可以添加一种新的角度去观测该对象,代码如下:


# 再定义一组旋转矩阵,以pitch轴进行b度旋转
b = 25
b = np.deg2rad(b)
R_pitch = np.array([
    [0, np.cos(b), -np.sin(b)],
    [1, 0, 0],
    [0, np.sin(b), np.cos(b)]
])
# 重新调整一下外参的旋转矩阵R
R = np.matmul(R_roll, R_pitch)
# 重新求基于相机坐标系的顶点集 加入yaw旋转角
vertices_c_pitch = np.matmul(R, vertices_w.T).T + T
# 继续使用内参K透视投影解出像素坐标的顶点集
vertices_uv_pitch = perspective_projection(vertices_c_pitch, K)
# 显示求解后的uv顶点集
display_vertices_uv(vertices_uv_pitch)

通过代码可得知我们在重新定义了一个外参矩阵R,是将之前定义的外参矩阵R_roll与当前新定义的R_pitch进行一个线性变换处理所得出的结果,其目的是让机位角在roll轴旋转45度的情况下再对其pitch轴旋转25度,得出该外参矩阵并进行坐标转换,结果如下:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

可以看到我们能观测到的立方体视角更加丰富了

示例2:使用解PnP求出外参矩阵

通过上述的案例,我们手动定义了相机的内外参矩阵,这里补充一点,在知道观测目标的世界坐标与相机内参的情况下,我们是可以使用一些数学手段去解出相机的外参矩阵的,这里采用的方法是使用opencv提供的solvePnP解法,去解出外参矩阵,接续上一个案例下进行代码的补充即可,代码如下:


# 使用solvePnP尝试解出相机外参
rvec = np.zeros((3, 1))
tvec = np.zeros((3, 1))

retval, rvec, tvec = cv2.solvePnP(vertices_w.astype(np.float32), vertices_uv_pitch.astype(np.float32),
                                  K.astype(np.float32),
                                  None, rvec, tvec, False, cv2.SOLVEPNP_ITERATIVE)
R_solved, _ = cv2.Rodrigues(rvec)
print("解PnP得出的R Matrix: ", -R_solved)    # 解出的坐标系是反着需要自行调整
print("自己定的R Matrix: ", R)

# 打印结果:
# 解PnP得出的R Matrix:  [[ 1.27441147e-04  9.06220345e-01 -4.22805713e-01]
#  [ 7.06805406e-01 -2.99177784e-01 -6.41029463e-01]
#  [ 7.07408017e-01  2.98759670e-01  6.40559566e-01]]
# 自己定的R Matrix:  [[ 0.          0.90630779 -0.42261826]
#  [ 0.70710678 -0.29883624 -0.64085638]
#  [ 0.70710678  0.29883624  0.64085638]]

可以看到,使用solvePnP解出的外参矩阵R与我们自行定义的矩阵R基本相等,该结果的精度与观测目标的顶点数量有关,顶点越多精度会越高。

示例3:旋转的立方体

通过上面的案例,我们可以写一个小玩意,通过不断的改变相机外参来实时更新并显示出被观测对象的画面,可让立方体仿佛动起来一样,代码如下:


# 定义方形画布像素坐标长度
canvas_square_size = 320
# 定义立方体的边长
length = 1

# 定义立方体的8个顶点坐标
vertices_w = np.array([
    [-length / 2, -length / 2, -length / 2],
    [-length / 2, -length / 2, length / 2],
    [-length / 2, length / 2, -length / 2],
    [-length / 2, length / 2, length / 2],
    [length / 2, -length / 2, -length / 2],
    [length / 2, -length / 2, length / 2],
    [length / 2, length / 2, -length / 2],
    [length / 2, length / 2, length / 2]])

# 手动定一组相机内参K
fx = 800
fy = 800
cx = canvas_square_size // 2
cy = canvas_square_size // 2
K = np.array([
    [fx, 0, cx],
    [0, fy, cy],
    [0, 0, 1]
])

# 初始化角度
a = 0
while True:
    # 手动定一个相机外参R旋转矩阵,并设置让三个轴旋转a度
    R = construct_extrinsic_matrix_R(a, a, a)

    # 手动定一个相机外参的偏移向量t 即在x, y, z的位置看面朝单位
    t1 = 0
    t2 = 0
    t3 = 5  # 数值越大则距离观测目标距离则越长
    T = np.array([t1, t2, t3])
    # 求基于相机坐标系的顶点集
    vertices_c = np.matmul(R, vertices_w.T).T + T
    # 使用透视投影解出像素坐标的顶点集
    vertices_uv = perspective_projection(vertices_c, K)
    # 显示求解后的uv顶点集
    display_vertices_uv(vertices_uv, wait_key=30, canvas_size=(canvas_square_size, canvas_square_size))
    a += 1

结果如下:

相机内参,3D视觉学习,算法,图形,图像算法,相机内参,相机外参,Powered by 金山文档

完整代码:

所有示例的完整代码如下:


import cv2
import numpy as np


def display_vertices_uv(vertices_2d, win_name='vertices', wait_key=0, canvas_size=(320, 320)):
    """Show the vertices on uv-coordinates"""
    img = np.zeros((canvas_size[0], canvas_size[1], 3), dtype=np.uint8)
    edges = np.array([
        [0, 1], [0, 2], [0, 4],
        [1, 3], [1, 5],
        [2, 3], [2, 6],
        [4, 5], [4, 6],
        [7, 5], [7, 6], [7, 3]])

    for edge in edges:
        pt1 = tuple(vertices_2d[edge[0]])
        pt2 = tuple(vertices_2d[edge[1]])
        cv2.line(img, pt1, pt2, (0, 255, 255), 2)

    cv2.imshow(win_name, img)
    cv2.waitKey(wait_key)


def perspective_projection(vertices, K):
    """use perspective projection"""
    vertices_2d = np.matmul(K, vertices.T).T
    vertices_2d[:, 0] /= vertices_2d[:, 2]
    vertices_2d[:, 1] /= vertices_2d[:, 2]
    vertices_2d = vertices_2d[:, :2].astype(np.int32)

    return vertices_2d


def construct_extrinsic_matrix_R(yaw_angle, roll_angle, pitch_angle):
    """Construct the camera external parameter rotation matrix R"""
    yaw = np.deg2rad(yaw_angle)
    roll = np.deg2rad(roll_angle)
    pitch = np.deg2rad(pitch_angle)
    R_yaw = np.array([
        [np.cos(yaw), -np.sin(yaw), 0],
        [np.sin(yaw), np.cos(yaw), 0],
        [0, 0, 1]
    ])
    R_roll = np.array([
        [1, 0, 0],
        [0, np.cos(roll), -np.sin(roll)],
        [0, np.sin(roll), np.cos(roll)]
    ])
    R_pitch = np.array([
        [0, np.cos(pitch), -np.sin(pitch)],
        [1, 0, 0],
        [0, np.sin(pitch), np.cos(pitch)]
    ])
    R = np.matmul(R_pitch, np.matmul(R_yaw, R_roll))

    return R


def sample_a():
    # 定义方形画布像素坐标长度
    canvas_square_size = 320
    # 定义立方体的边长
    length = 1

    # 定义立方体的8个顶点坐标 使用世界坐标作为表达
    vertices_w = np.array([
        [-length / 2, -length / 2, -length / 2],
        [-length / 2, -length / 2, length / 2],
        [-length / 2, length / 2, -length / 2],
        [-length / 2, length / 2, length / 2],
        [length / 2, -length / 2, -length / 2],
        [length / 2, -length / 2, length / 2],
        [length / 2, length / 2, -length / 2],
        [length / 2, length / 2, length / 2]])
    print("世界坐标系顶点集合: ", vertices_w.shape)

    # 定义一个角度
    a = 45
    # 转换为弧度制
    a = np.deg2rad(a)

    # 手动定一个相机外参R旋转矩阵,并设置让其绕roll轴旋转a度
    R_roll = np.array([
        [1, 0, 0],
        [0, np.cos(a), -np.sin(a)],
        [0, np.sin(a), np.cos(a)]
    ])
    # 手动定一个相机外参的偏移向量t 即在x, y, z的位置看面朝单位
    t1 = 0
    t2 = 0
    t3 = 5  # 数值越大则距离观测目标距离则越长
    T = np.array([t1, t2, t3])

    # 求基于相机坐标系的顶点集
    vertices_c = np.matmul(R_roll, vertices_w.T).T + T

    # 手动定一组相机内参K
    fx = 800
    fy = 800
    cx = canvas_square_size // 2
    cy = canvas_square_size // 2
    K = np.array([
        [fx, 0, cx],
        [0, fy, cy],
        [0, 0, 1]
    ])

    # 使用透视投影解出像素坐标的顶点集
    vertices_uv = perspective_projection(vertices_c, K)
    print("像素坐标系顶点集合: ", vertices_uv.shape)

    # 显示求解后的uv顶点集
    display_vertices_uv(vertices_uv, canvas_size=(canvas_square_size, canvas_square_size))

    # 再定义一组旋转矩阵,以pitch轴进行b度旋转
    b = 25
    b = np.deg2rad(b)
    R_pitch = np.array([
        [0, np.cos(b), -np.sin(b)],
        [1, 0, 0],
        [0, np.sin(b), np.cos(b)]
    ])
    # 重新调整一下外参的旋转矩阵R
    R = np.matmul(R_roll, R_pitch)
    # 重新求基于相机坐标系的顶点集 加入yaw旋转角
    vertices_c_pitch = np.matmul(R, vertices_w.T).T + T
    # 继续使用内参K透视投影解出像素坐标的顶点集
    vertices_uv_pitch = perspective_projection(vertices_c_pitch, K)
    # 显示求解后的uv顶点集
    display_vertices_uv(vertices_uv_pitch)

    # 使用solvePnP尝试解出相机外参
    rvec = np.zeros((3, 1))
    tvec = np.zeros((3, 1))

    retval, rvec, tvec = cv2.solvePnP(vertices_w.astype(np.float32), vertices_uv_pitch.astype(np.float32),
                                      K.astype(np.float32),
                                      None, rvec, tvec, False, cv2.SOLVEPNP_ITERATIVE)
    R_solved, _ = cv2.Rodrigues(rvec)
    print("解PnP得出的R Matrix: ", -R_solved)   # 解出的坐标系是反着需要自行调整
    print("自己定的R Matrix: ", R)


def sample_b():
    # 定义方形画布像素坐标长度
    canvas_square_size = 320
    # 定义立方体的边长
    length = 1

    # 定义立方体的8个顶点坐标
    vertices_w = np.array([
        [-length / 2, -length / 2, -length / 2],
        [-length / 2, -length / 2, length / 2],
        [-length / 2, length / 2, -length / 2],
        [-length / 2, length / 2, length / 2],
        [length / 2, -length / 2, -length / 2],
        [length / 2, -length / 2, length / 2],
        [length / 2, length / 2, -length / 2],
        [length / 2, length / 2, length / 2]])

    # 手动定一组相机内参K
    fx = 800
    fy = 800
    cx = canvas_square_size // 2
    cy = canvas_square_size // 2
    K = np.array([
        [fx, 0, cx],
        [0, fy, cy],
        [0, 0, 1]
    ])

    # 初始化角度
    a = 0
    while True:
        # 手动定一个相机外参R旋转矩阵,并设置让三个轴旋转a度
        R = construct_extrinsic_matrix_R(a, a, a)

        # 手动定一个相机外参的偏移向量t 即在x, y, z的位置看面朝单位
        t1 = 0
        t2 = 0
        t3 = 5  # 数值越大则距离观测目标距离则越长
        T = np.array([t1, t2, t3])
        # 求基于相机坐标系的顶点集
        vertices_c = np.matmul(R, vertices_w.T).T + T
        # 使用透视投影解出像素坐标的顶点集
        vertices_uv = perspective_projection(vertices_c, K)
        # 显示求解后的uv顶点集
        display_vertices_uv(vertices_uv, wait_key=30, canvas_size=(canvas_square_size, canvas_square_size))
        a += 1


if __name__ == '__main__':
    sample_a()  # 示例1
    sample_b()  # 示例2

Note:

由于忘性较大,仓促实验和匆忙记录只为记录自己的理解和实验经过留后期继续学习和观看,可能会有不少理解错误,如果给您带来困扰烦请指正和海涵。文章来源地址https://www.toymoban.com/news/detail-610356.html

到了这里,关于关于相机内参与外参的浅读的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 计算机视觉(相机标定;内参;外参;畸变系数)

    目录 一、预备知识 1、坐标系变换过程(相机成像过程) (1)相机坐标系转换为图像坐标系(透视投影变换遵循的是针孔成像原理) (2)齐次坐标的引入原因:(为什么引入齐次坐标???) 2、内参与外参矩阵的构成 3、畸变参数 二、相机标定 1、张正友标定法(光学标

    2024年02月07日
    浏览(38)
  • (九)相机内参、外参、反透视变换python opencv

    任务需求:将相机上的一个点投影到真实世界平面上去。 原则上单目相机是不可以的,因为只记录了二维信息,真实世界是三维的,双目相机可以通过视差,或者单目+IMU组合,但是 由于特征点在地面上的先验知识 ,因此可以进行反透视变换。方法有很多种那个,这里采用计

    2024年02月06日
    浏览(35)
  • 一文理清相机内参和外参矩阵的来龙去脉 -- review向

    Auther:SeaHIRobot date: 2023-05-03 最近在做一门课程的项目,在pybullet中复现GGCNN的机械臂视觉抓取的深度学习网络。在搭建仿真环境时,又回到了绕不开的相机内参和外参。借此机会对相机内参外参这一套进行一个review,对这个问题进行一个逻辑梳理。 因此本文不建议纯小白阅读

    2024年02月04日
    浏览(37)
  • 使用ROS功能包camera_calibration进行单目相机和双目相机的内参和外参标定

    本文总结使用ROS标定单目和双目相机的过程,同时提供生成棋盘格文件的方法。 参考链接: [1]使用ros标定相机的内参和外参 [2]ROS下采用camera_calibration进行双目相机标定 棋盘格可以自己买一个,或者打印一个粘在板子上,棋盘格电子版生成可以参考博客《使用kalibr标定工具进

    2024年02月11日
    浏览(32)
  • 数字图像处理 --- 相机的内参与外参(CV学习笔记)

    Pinhole Camera Model(针孔相机模型)         针孔相机是一种没有镜头、只有一个小光圈的简单相机。 光线穿过光圈并在相机的另一侧呈现倒立的图像。为了建模方便,我们可以把 物理成像平面 (image plane)上的图像移到 实际场景 (3D object)和 焦点 (focal point)之间,把他想象成

    2024年02月12日
    浏览(30)
  • c++学习【13】3D-2D高斯牛顿BA迭代求解相机外参

    pc表示当前坐标, 通过将 points_3d[i] 与 pose 相乘,得到的结果 pc 是该点在相机坐标系下的表示。 proj 是一个二维向量,表示在相机坐标系下,通过相机投影将三维点投影到二维平面上得到的坐标。 inv_z在绑定调整迭代求解相机位姿中,用于后续的优化目标函数计算或者高斯牛

    2024年02月11日
    浏览(24)
  • 标定(内参、外参)

    在计算机视觉中,特别是在相机标定和立体视觉领域,内参(intrinsic parameters)和外参(extrinsic parameters)是非常重要的概念。它们与相机的几何属性和姿态有关。 内参(Intrinsic Parameters) : 内参是描述相机内部属性的参数,包括焦距、主点(光学中心)坐标、畸变系数等。

    2024年02月14日
    浏览(29)
  • 激光雷达与相机外参标定(附open3d python代码)

    现在的激光雷达与相机的标定程序基本都是Ubuntu框架下面的,并且都是C++代码,需要安装的依赖也比较复杂,于是自己写了一个python版本的标定程序,依赖非常简单,Windows系统也可以运行。并且代码简单一个文件搞定,符合python简单易行的风格。 先上最后标定后的效果图​

    2024年02月11日
    浏览(28)
  • 形参与实参的主要区别

    形参(parameter) :形参(形式参数)是在函数定义中出现的参数,是一个虚拟参数,调用之前并没有给他分配内存,可以看作是一个占位符,在函数定义时没有实际的数值,只有在函数调用时才会接收到传递进来的数据;只是因为函数需要从别处传递数据,为了表示此数据,

    2024年02月05日
    浏览(39)
  • 【相机标定】相机内参

    相机在计算机视觉方面的一些应用一般需要相机标定。我们总是听到标定这个词,那么具体标定的是什么呢?相机的拍摄是一个三维到二维(透视投影)的过程,这个过程可以用数学模型去表述,标定便是计算这个数学模型中的参数,我们最终希望通过这些参数能够从二维的

    2023年04月10日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包