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

时间:2015-07-04 18:57:22

标签: math matrix 3d projection matrix-inverse

给定每个图像的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

我正在使用" DinoSparseRing"数据集http://vision.middlebury.edu/mview/data

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个图像)。蓝色端是摄像机点,青色是包含摄像机捕获的对象的边界框。人们可以清楚地看到光线在世界坐标系中投射回物体。

enter image description here enter image description here

1 个答案:

答案 0 :(得分:1)

要定义光线,您需要一个起点(相机/眼睛位置)和一个方向矢量,可以使用光线上的任何一点计算。

对于图像中的给定像素,您有一个投影的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平面相交。