Update the trunk to the OpenCV's CVS (2008-07-14)
[opencv] / cv / src / cvgeometry.cpp
index 4091c10..0b3648c 100644 (file)
@@ -351,5 +351,270 @@ cvPointPolygonTest( const CvArr* _contour, CvPoint2D32f pt, int measure_dist )
 }
 
 
-/* End of file. */
+#define CV_VERYSMALLDOUBLE 1.0e-10
+
+CV_IMPL void
+cvRQDecomp3x3( const CvMat *matrixM, CvMat *matrixR, CvMat *matrixQ,
+               CvMat *matrixQx, CvMat *matrixQy, CvMat *matrixQz,
+               CvPoint3D64f *eulerAngles)
+{
+       CvMat *tmpMatrix1 = 0;
+       CvMat *tmpMatrix2 = 0;
+       CvMat *tmpMatrixM = 0;
+       CvMat *tmpMatrixR = 0;
+       CvMat *tmpMatrixQ = 0;
+       CvMat *tmpMatrixQx = 0;
+       CvMat *tmpMatrixQy = 0;
+       CvMat *tmpMatrixQz = 0;
+       double tmpEulerAngleX, tmpEulerAngleY, tmpEulerAngleZ;
+       
+       CV_FUNCNAME("cvRQDecomp3x3");
+    __BEGIN__;
+       
+       /* Validate parameters. */
+       if(matrixM == 0 || matrixR == 0 || matrixQ == 0)
+               CV_ERROR(CV_StsNullPtr, "Some of parameters is a NULL pointer!");
+       
+       if(!CV_IS_MAT(matrixM) || !CV_IS_MAT(matrixR) || !CV_IS_MAT(matrixQ))
+               CV_ERROR(CV_StsUnsupportedFormat, "Input parameters must be a matrices!");
+       
+       if(matrixM->cols != 3 || matrixM->rows != 3 || matrixR->cols != 3 || matrixR->rows != 3 || matrixQ->cols != 3 || matrixQ->rows != 3)
+               CV_ERROR(CV_StsUnmatchedSizes, "Size of matrices must be 3x3!");
+       
+       CV_CALL(tmpMatrix1 = cvCreateMat(3, 3, CV_64F));
+       CV_CALL(tmpMatrix2 = cvCreateMat(3, 3, CV_64F));
+       CV_CALL(tmpMatrixM = cvCreateMat(3, 3, CV_64F));
+       CV_CALL(tmpMatrixR = cvCreateMat(3, 3, CV_64F));
+       CV_CALL(tmpMatrixQ = cvCreateMat(3, 3, CV_64F));
+       CV_CALL(tmpMatrixQx = cvCreateMat(3, 3, CV_64F));
+       CV_CALL(tmpMatrixQy = cvCreateMat(3, 3, CV_64F));
+       CV_CALL(tmpMatrixQz = cvCreateMat(3, 3, CV_64F));
+       
+       cvCopy(matrixM, tmpMatrixM);
+       
+       /* Find Givens rotation Q_x for x axis. */
+       /*
+             ( 1  0  0 )
+        Qx = ( 0  c -s ), cos = -m33/sqrt(m32^2 + m33^2), cos = m32/sqrt(m32^2 + m33^2)
+                 ( 0  s  c )
+        */
+       
+       double x, y, z, c, s;
+       x = cvmGet(tmpMatrixM, 2, 1);
+       y = cvmGet(tmpMatrixM, 2, 2);
+       z = x * x + y * y;
+       assert(z != 0); // Prevent division by zero.
+       c = -y / sqrt(z);
+       s = x / sqrt(z);
+       
+       cvSetIdentity(tmpMatrixQx);
+       cvmSet(tmpMatrixQx, 1, 1, c);
+       cvmSet(tmpMatrixQx, 1, 2, -s);
+       cvmSet(tmpMatrixQx, 2, 1, s);
+       cvmSet(tmpMatrixQx, 2, 2, c);
+       
+       tmpEulerAngleX = acos(c) * 180.0 / CV_PI;
+       
+       /* Multiply M on the right by Q_x. */
+       
+       cvMatMul(tmpMatrixM, tmpMatrixQx, tmpMatrixR);
+       cvCopy(tmpMatrixR, tmpMatrixM);
+       
+       assert(cvmGet(tmpMatrixM, 2, 1) < CV_VERYSMALLDOUBLE && cvmGet(tmpMatrixM, 2, 1) > -CV_VERYSMALLDOUBLE); // Should actually be zero.
+       
+       if(cvmGet(tmpMatrixM, 2, 1) != 0.0)
+               cvmSet(tmpMatrixM, 2, 1, 0.0); // Rectify arithmetic precision error.
+       
+       /* Find Givens rotation for y axis. */
+       /*
+             ( c  0  s )
+        Qy = ( 0  1  0 ), cos = m33/sqrt(m31^2 + m33^2), cos = m31/sqrt(m31^2 + m33^2)
+             (-s  0  c )
+        */
+       
+       x = cvmGet(tmpMatrixM, 2, 0);
+       y = cvmGet(tmpMatrixM, 2, 2);
+       z = x * x + y * y;
+       assert(z != 0); // Prevent division by zero.
+       c = y / sqrt(z);
+       s = x / sqrt(z);
+       
+       cvSetIdentity(tmpMatrixQy);
+       cvmSet(tmpMatrixQy, 0, 0, c);
+       cvmSet(tmpMatrixQy, 0, 2, s);
+       cvmSet(tmpMatrixQy, 2, 0, -s);
+       cvmSet(tmpMatrixQy, 2, 2, c);
+       
+       tmpEulerAngleY = acos(c) * 180.0 / CV_PI;
+       
+       /* Multiply M*Q_x on the right by Q_y. */
+       
+       cvMatMul(tmpMatrixM, tmpMatrixQy, tmpMatrixR);
+       cvCopy(tmpMatrixR, tmpMatrixM);
+       
+       assert(cvmGet(tmpMatrixM, 2, 0) < CV_VERYSMALLDOUBLE && cvmGet(tmpMatrixM, 2, 0) > -CV_VERYSMALLDOUBLE); // Should actually be zero.
+       
+       if(cvmGet(tmpMatrixM, 2, 0) != 0.0)
+               cvmSet(tmpMatrixM, 2, 0, 0.0); // Rectify arithmetic precision error.
+       
+       /* Find Givens rotation for z axis. */
+       /*
+             ( c -s  0 )
+        Qz = ( s  c  0 ), cos = -m22/sqrt(m21^2 + m22^2), cos = m21/sqrt(m21^2 + m22^2)
+             ( 0  0  1 )
+        */
+       
+       x = cvmGet(tmpMatrixM, 1, 0);
+       y = cvmGet(tmpMatrixM, 1, 1);
+       z = x * x + y * y;
+       assert(z != 0); // Prevent division by zero.
+       c = -y / sqrt(z);
+       s = x / sqrt(z);
+       
+       cvSetIdentity(tmpMatrixQz);
+       cvmSet(tmpMatrixQz, 0, 0, c);
+       cvmSet(tmpMatrixQz, 0, 1, -s);
+       cvmSet(tmpMatrixQz, 1, 0, s);
+       cvmSet(tmpMatrixQz, 1, 1, c);
+       
+       tmpEulerAngleZ = acos(c) * 180.0 / CV_PI;
+       
+       /* Multiply M*Q_x*Q_y on the right by Q_z. */
+       
+       cvMatMul(tmpMatrixM, tmpMatrixQz, tmpMatrixR);
+       
+       assert(cvmGet(tmpMatrixR, 1, 0) < CV_VERYSMALLDOUBLE && cvmGet(tmpMatrixR, 1, 0) > -CV_VERYSMALLDOUBLE); // Should actually be zero.
+       
+       if(cvmGet(tmpMatrixR, 1, 0) != 0.0)
+               cvmSet(tmpMatrixR, 1, 0, 0.0); // Rectify arithmetic precision error.
+       
+       /* Calulate orthogonal matrix. */
+       /*
+        Q = QzT * QyT * QxT
+        */
+       
+       cvTranspose(tmpMatrixQz, tmpMatrix1);
+       cvTranspose(tmpMatrixQy, tmpMatrix2);
+       cvMatMul(tmpMatrix1, tmpMatrix2, tmpMatrixQ);
+       cvCopy(tmpMatrixQ, tmpMatrix1);
+       cvTranspose(tmpMatrixQx, tmpMatrix2);
+       cvMatMul(tmpMatrix1, tmpMatrix2, tmpMatrixQ);
+       
+       /* Solve decomposition ambiguity. */
+       /*
+        Diagonal entries of R should be positive, so swap signs if necessary.
+        */
+       
+       if(cvmGet(tmpMatrixR, 0, 0) < 0.0) {
+               cvmSet(tmpMatrixR, 0, 0, -1.0 * cvmGet(tmpMatrixR, 0, 0));
+               cvmSet(tmpMatrixQ, 0, 0, -1.0 * cvmGet(tmpMatrixQ, 0, 0));
+               cvmSet(tmpMatrixQ, 0, 1, -1.0 * cvmGet(tmpMatrixQ, 0, 1));
+               cvmSet(tmpMatrixQ, 0, 2, -1.0 * cvmGet(tmpMatrixQ, 0, 2));
+       }
+       if(cvmGet(tmpMatrixR, 1, 1) < 0.0) {
+               cvmSet(tmpMatrixR, 0, 1, -1.0 * cvmGet(tmpMatrixR, 0, 1));
+               cvmSet(tmpMatrixR, 1, 1, -1.0 * cvmGet(tmpMatrixR, 1, 1));
+               cvmSet(tmpMatrixQ, 1, 0, -1.0 * cvmGet(tmpMatrixQ, 1, 0));
+               cvmSet(tmpMatrixQ, 1, 1, -1.0 * cvmGet(tmpMatrixQ, 1, 1));
+               cvmSet(tmpMatrixQ, 1, 2, -1.0 * cvmGet(tmpMatrixQ, 1, 2));
+       }
+       
+       /* Save R and Q matrices. */
+       
+       cvCopy(tmpMatrixR, matrixR);
+       cvCopy(tmpMatrixQ, matrixQ);
+       
+       if(matrixQx && CV_IS_MAT(matrixQx) && matrixQx->cols == 3 || matrixQx->rows == 3)
+               cvCopy(tmpMatrixQx, matrixQx);
+       if(matrixQy && CV_IS_MAT(matrixQy) && matrixQy->cols == 3 || matrixQy->rows == 3)
+               cvCopy(tmpMatrixQy, matrixQy);
+       if(matrixQz && CV_IS_MAT(matrixQz) && matrixQz->cols == 3 || matrixQz->rows == 3)
+               cvCopy(tmpMatrixQz, matrixQz);
+       
+       /* Save Euler angles. */
+       
+       if(eulerAngles)
+               *eulerAngles = cvPoint3D64f(tmpEulerAngleX, tmpEulerAngleY, tmpEulerAngleZ);
+       
+       __END__;
+       
+       cvReleaseMat(&tmpMatrix1);
+       cvReleaseMat(&tmpMatrix2);
+       cvReleaseMat(&tmpMatrixM);
+       cvReleaseMat(&tmpMatrixR);
+       cvReleaseMat(&tmpMatrixQ);
+       cvReleaseMat(&tmpMatrixQx);
+       cvReleaseMat(&tmpMatrixQy);
+       cvReleaseMat(&tmpMatrixQz);
+
+}
 
+
+CV_IMPL void
+cvDecomposeProjectionMatrix( const CvMat *projMatr, CvMat *calibMatr,
+                             CvMat *rotMatr, CvMat *posVect,
+                             CvMat *rotMatrX, CvMat *rotMatrY,
+                             CvMat *rotMatrZ, CvPoint3D64f *eulerAngles)
+{
+       CvMat *tmpProjMatr = 0;
+       CvMat *tmpMatrixD = 0;
+       CvMat *tmpMatrixV = 0;
+       CvMat *tmpMatrixM = 0;
+       
+       CV_FUNCNAME("cvDecomposeProjectionMatrix");
+    __BEGIN__;
+       
+       /* Validate parameters. */
+       if(projMatr == 0 || calibMatr == 0 || rotMatr == 0 || posVect == 0)
+               CV_ERROR(CV_StsNullPtr, "Some of parameters is a NULL pointer!");
+       
+       if(!CV_IS_MAT(projMatr) || !CV_IS_MAT(calibMatr) || !CV_IS_MAT(rotMatr) || !CV_IS_MAT(posVect))
+               CV_ERROR(CV_StsUnsupportedFormat, "Input parameters must be a matrices!");
+       
+       if(projMatr->cols != 4 || projMatr->rows != 3)
+               CV_ERROR(CV_StsUnmatchedSizes, "Size of projection matrix must be 3x4!");
+       
+       if(calibMatr->cols != 3 || calibMatr->rows != 3 || rotMatr->cols != 3 || rotMatr->rows != 3)
+               CV_ERROR(CV_StsUnmatchedSizes, "Size of calibration and rotation matrices must be 3x3!");
+       
+       if(posVect->cols != 1 || posVect->rows != 4)
+               CV_ERROR(CV_StsUnmatchedSizes, "Size of position vector must be 4x1!");
+       
+       CV_CALL(tmpProjMatr = cvCreateMat(4, 4, CV_64F));
+       CV_CALL(tmpMatrixD = cvCreateMat(4, 4, CV_64F));
+       CV_CALL(tmpMatrixV = cvCreateMat(4, 4, CV_64F));
+       CV_CALL(tmpMatrixM = cvCreateMat(3, 3, CV_64F));
+       
+       /* Compute position vector. */
+       
+       cvSetZero(tmpProjMatr); // Add zero row to make matrix square.
+       int i, k;
+       for(i = 0; i < 3; i++)
+               for(k = 0; k < 4; k++)
+                       cvmSet(tmpProjMatr, i, k, cvmGet(projMatr, i, k));
+       
+       CV_CALL(cvSVD(tmpProjMatr, tmpMatrixD, NULL, tmpMatrixV, CV_SVD_MODIFY_A + CV_SVD_V_T));
+       
+       /* Save position vector. */
+       
+       for(i = 0; i < 4; i++)
+               cvmSet(posVect, i, 0, cvmGet(tmpMatrixV, 3, i)); // Solution is last row of V.
+       
+       /* Compute calibration and rotation matrices via RQ decomposition. */
+       
+       cvGetCols(projMatr, tmpMatrixM, 0, 3); // M is first square matrix of P.
+       
+       assert(cvDet(tmpMatrixM) != 0.0); // So far only finite cameras could be decomposed, so M has to be nonsingular [det(M) != 0].
+       
+       CV_CALL(cvRQDecomp3x3(tmpMatrixM, calibMatr, rotMatr, rotMatrX, rotMatrY, rotMatrZ, eulerAngles));
+       
+       __END__;
+       
+       cvReleaseMat(&tmpProjMatr);
+       cvReleaseMat(&tmpMatrixD);
+       cvReleaseMat(&tmpMatrixV);
+       cvReleaseMat(&tmpMatrixM);
+       
+}
+
+/* End of file. */