我有一个家庭作业,必须使用透视变换来计算和绘制一些点,但是我不确定我的结果是正确的,因为使用相机坐标的3d图看起来与使用图像坐标的2d图非常不同。

这是给定的:
相机位于指定的点$ _WT ^ C = [−1,1,5] ^ T $以世界坐标(以米为单位)。相机坐标系绕世界参考的Y轴旋转$ \ theta = 160 ^ o $,因此它的旋转矩阵为$ ^ wR_c = \ begin {bmatrix} cos(\ theta)&0&sin(\ theta )\\ 0&1&0 \\ -sin(\ theta)&0&cos(\ theta)\ end {bmatrix} $

相机参数为:$ f = 16mm $,$ s_x = s_y = 0.01 mm / px $,$ o_x = 320 px $,$ o_y = 240px $

采样点(在世界坐标中):

$ ^ WP_1 = [ 1,1,0.5] ^ T $

$ ^ WP_2 = [1,1.5,0.5] ^ T $

$ ^ WP_3 = [1.5,1.5,0.5] ^ T $

$ ^ WP_4 = [1.5,1,0.5] ^ T $

我必须计算并绘制相机坐标系和图像坐标系中的点,因此我在八度中编写了以下代码:来自脚本:
我期望它们有些相似,但看起来却不太相似。



在相机坐标中绘制图



在图像c中绘制坐标

评论

+1表示作业可以是高质量的问题。 :)

正如在meta上指出的,这个问题值得一个很好的答案。我自己一个人,但我很乐意将自己的名声传给有成就的人。
@trichoplax问题在于它是在matlab中完成的。

@joojaa啊好点。如果在赏金期间没有任何matlab专家介入,我将考虑学习Octave,以了解是否足够接近找到解决方案。

我不清楚第一张图片的含义。第二个是从相机的角度来看,经过包络估计后,我认为它看起来是正确的。

#1 楼

在两个图形中都确定轴并将相机位置添加到第一个图形中将有助于您了解发生了什么。作为每个点和列作为分量$ x $,$ y $和$ z $。这样,您可以使用简单的矩阵乘法来处理投影,而不必分别处理每一行。

在问题陈述中,了解旋转方向非常有趣,更重要的是,了解旋转方向相机方向及其向上向量。我猜您的相机逆时针旋转了160°,原始相机方向为$ [0,0,1] $,其向上矢量为$ [0,1,0] $。如果任何这些假设是错误的,那么其余答案将是错误的。

在您的代码中没有mm到m转换的迹象。您的焦点应该是$ 0.016 $,而$ S_x = S_y = 0.0001 $,或者是将点坐标乘以$ 0.00001 $。例如,您的相机中心没有旋转就指向$ [-1,1,x] $线。由于这些点都在$ z = 0.5 $平面上,因此可以进行以下分析:如果我们关注$ x $轴,则可以看到$ tan(160°)\ cdot(5-0.5)= 1.64 ... $,因此摄像头中心将稍稍靠近点的左侧(因为摄像头位于$ x = -1 $),因此摄像头中心将最终以$ \约0.64 $结束,这意味着点将出现在图像的右侧。此外,相机具有与两个点相同的$ y $坐标,并且由于$ y $坐标不会因旋转而改变,因此变换后它们仍应最终位于相同的坐标上,这意味着图像的中心行。

检查现有答案的好方法是使用现有的3D建模器,例如Blender:

注意Blender的坐标系,例如默认相机矢量为[0, 0, -1]
这是渲染器:

将“焦点”设置为另一个值,以使球体更加可见。因此,我们看到底部的两个点位于图像的中间行,并且这些点略微位于图像的右侧。 >
import numpy as np

from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import axes3d, Axes3D


# Parameters
f_mm = 0.016
f_px = f_mm / 0.00001
t_cam = np.array([[-1., 1., 5.]]).T
t_cam_homogeneous = np.vstack((t_cam, np.array([[0]])))
theta = 160. * np.pi / 180.
ox = 320
oy = 240
# Rotation and points are in homogeneous coordinates
rot_cam = np.array([[np.cos(theta), 0, np.sin(theta)],
                    [0, 1, 0],
                    [-np.sin(theta), 0, np.cos(theta)]])
points = np.array([[1, 1, 0.5, 1],
                   [1, 1.5, 0.5, 1],
                   [1.5, 1.5, 0.5, 1],
                   [1.5, 1, 0.5, 1]]).T

# Compute projection matrix using intrinsics and extrinsics
intrinsics = np.array([[f_px, 0, ox],
                       [0, f_px, oy],
                       [0, 0, 1]])
extrinsics = np.hstack((rot_cam, rot_cam.dot(-t_cam)))

rot_cam2 = np.identity(4); rot_cam2[:3,:3] = rot_cam
camera_coordinates = rot_cam2.dot(points - t_cam_homogeneous)
camera_coordinates = camera_coordinates[:3,:] / camera_coordinates[3,:]

# Perform the projection
projected_points = intrinsics.dot(camera_coordinates)
projected_points = projected_points[:2,:] / projected_points[2,:]
projected_points[0,:] = -projected_points[0,:] # Inverted x-axis because camera is pointing toward [0, 0, 1]

fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(points[0,:], points[1,:], points[2,:], label="Points")
ax.scatter(t_cam[0], t_cam[1], t_cam[2], c="red", label="Camera")
ax.set_xlabel("X axis"); ax.set_ylabel("Y axis"); ax.set_zlabel("Z axis")
plt.title("World coordinates")
plt.legend()
plt.savefig('world_coordinates.png', dpi=300, bbox_inches="tight")

fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(camera_coordinates[0,:], camera_coordinates[1,:], camera_coordinates[2,:], label="Points")
ax.scatter(0, 0, 0, c="red", label="Camera")
ax.set_xlabel("X axis"); ax.set_ylabel("Y axis"); ax.set_zlabel("Z axis")
plt.title("Camera coordinates")
plt.legend()
plt.savefig('camera_coordinates.png', dpi=300, bbox_inches="tight")

plt.figure()
plt.scatter(projected_points[0,:], projected_points[1,:])
plt.xlabel("X axis"); plt.ylabel("Y axis")
plt.title("Image coordinates")
plt.savefig('image_coordinates.png', dpi=300, bbox_inches="tight")

plt.show()


这给了我这些数字:




分别:世界坐标,摄像机坐标,摄像机坐标旋转以略微适合摄影机方向(请注意,摄影机矢量朝着图形视点移动,不会“输入”图形)和图像坐标。底部的点正确地位于中间行(240)上,并且这些点位于图像的右侧(水平值> 320)。

我相信您遇到的一个错误是您发现X值为负,因此您求反了内在矩阵中的焦点(-f/Sxy)以进行补偿。这里的问题是我们假设相机最初指向$ [0,0,1] $(否则160°旋转将不会指向这些点)。如果以这种方式查看,则$ x $轴在向左移动时会增加,因此应取该轴的倒数。

我们的结果似乎都与我相似,只是您认为相机的$ [0,-1,0] $向上向量(实际上两个轴都被镜像,因为您取消了两个焦点),并且以mm而不是米进行计算。