如何使用投影矩阵从图像计算现实世界坐标系中的射线?

How to calculate ray in real-world coordinate system from image using projection matrix?

给定 n 个图像和每个图像的投影矩阵,我如何计算图像的每个像素发出的射线(线),它与现实世界坐标系的三个平面之一相交?相机拍摄的物体在同一位置,只是每张图像的相机位置不同。这就是为什么每个图像都有一个单独的投影矩阵。

据我的研究表明,这是 3D 到 2D 投影的反转。由于投影到2D时会丢失信息,所以只能在现实世界坐标系中计算射线(线),这样就可以了。

一个示例投影矩阵 P,根据给定的 K、R 和 t 分量计算,根据 K*[R t]

   3310.400000 0.000000 316.730000 
K= 0.000000 3325.500000 200.550000 
   0.000000 0.000000 1.000000 


   -0.14396457836077139000 0.96965263281337499000 0.19760617153779569000 
R= -0.90366580603479685000 -0.04743335255026152200 -0.42560419233334673000 
   -0.40331536459778505000 -0.23984130575212276000 0.88306936201487163000 

   -0.010415508744 
t= -0.0294278883669 
   0.673097816109

   -604.322  3133.973   933.850   178.711
P= -3086.026  -205.840 -1238.247    37.127
   -0.403    -0.240     0.883     0.673

我正在使用 http://vision.middlebury.edu/mview/data

上可用的 "DinoSparseRing" 数据集
for (int i = 0; i < 16; i++) {
        RealMatrix rotationMatrix = MatrixUtils.createRealMatrix(rotationMatrices[i]);
        RealVector translationVector = MatrixUtils.createRealVector(translationMatrices[i]);
        // construct projection matrix according to K*[R t]
        RealMatrix projMatrix = getP(kalibrationMatrices[i], rotationMatrices[i], translationMatrices[i]);
        // getM returns the first 3x3 block of the 3x4 projection matrix
        RealMatrix projMInverse = MatrixUtils.inverse(getM(projMatrix));
        // compute camera center
        RealVector c = rotationMatrix.transpose().scalarMultiply(-1.f).operate(translationVector);

        // compute all unprojected points and direction vector per project point
        for (int m = 0; m < image_m_num_pixel; m++) {
            for (int n = 0; n < image_n_num_pixel; n++) {
                double[] projectedPoint = new double[]{
                        n,
                        m,
                        1};
                // undo perspective divide
                projectedPoint[0] *= projectedPoint[2];
                projectedPoint[1] *= projectedPoint[2];
                // undo projection by multiplying by inverse:
                RealVector projectedPointVector = MatrixUtils.createRealVector(projectedPoint);
                RealVector unprojectedPointVector = projMInverse.operate(projectedPointVector);

                // compute direction vector
                RealVector directionVector = unprojectedPointVector.subtract(c);
                // normalize direction vector
                double dist = Math.sqrt((directionVector.getEntry(0) * directionVector.getEntry(0))
                        + (directionVector.getEntry(1) * directionVector.getEntry(1))
                        + (directionVector.getEntry(2) * directionVector.getEntry(2)));
                directionVector.setEntry(0, directionVector.getEntry(0) * (1.0 / dist));
                directionVector.setEntry(1, directionVector.getEntry(1) * (1.0 / dist));
                directionVector.setEntry(2, directionVector.getEntry(2) * (1.0 / dist));
            }
        }
    }

以下 2 个图显示了每张图像的外部光线(总共 16 张图像)。蓝色端是相机点,青色是包含相机捕获的对象的边界框。可以清楚地看到光线投射回世界坐标系中的物体。

要定义射线,您需要一个起点(即 camera/eye 位置)和一个方向向量,可以使用射线上的任意点计算。

对于图像中的给定像素,您有投影的 X 和 Y(在图像中心归零)但没有 Z 深度值。然而,对应于该像素所有可能深度值的真实世界坐标都位于您要计算的射线上,因此您可以选择任意非零 Z 值,因为射线上的任何点都可以.

float projectedX = (x - imageCenterX) / (imageWidth * 0.5f);
float projectedY = (y - imageCenterY) / (imageHeight * 0.5f);
float projectedZ = 1.0f; // any arbitrary value

现在您有了一个 3D 投影坐标,您可以通过将 X 和 Y 乘以 Z,然后将结果乘以逆投影矩阵来获得未投影点,从而反向应用透视划分来撤消投影。

// undo perspective divide (redundant if projectedZ = 1, shown for completeness)
projectedX *= projectedZ;
projectedY *= projectedZ;
Vector3 projectedPoint = new Vector3(projectedX, projectedY, projectedZ);

// undo projection by multiplying by inverse:
Matrix invProjectionMat = projectionMat.inverse();
Vector3 unprojectedPoint = invProjectionMat.multiply(projectedPoint);

未投影的点减去相机位置,得到相机到该点的方向向量,然后归一化。 (这一步假设投影矩阵同时定义了相机位置和方向,如果位置单独存储则不需要做减法)

Vector3 directionVector = unprojectedPoint.subtract(cameraPosition);
directionVector.normalize();

光线由相机位置和归一化方向向量定义。然后您可以将它与任何 X、Y、Z 平面相交。