zoukankan      html  css  js  c++  java
  • OpenCV-3D学习日志:生成立体相机

    1.本文要点说明

    1.OpenCV双目相机

             (1)左右位姿变换关系:所得变换是左相机到右相机的变换,可将左相机看作第一次观察而右相机看第二次观察,若是多目相机则继续第三次第四次等观察

             (2)左右位姿公式关系:

             (3)左右位姿矢量关系:左相机指定右相机=世界指向右相机*左相机指定世界

     

             (4)生成立体相机:首先使左右像平面平行,然后使左右像平面的X轴重合(针对水平极线约束),最后还可以添加一个自定义的旋转以在期望的角度成像,注意只能旋转而不能平移

                      1)左右像平面平行要求rB,A=0:由矢量关系图可知这就要求rA+rB=0,为保证左右共视区域最大,显然最佳解是rA=0.5r和rB=-0.5r,此时可解得tB,A=RBt

                      2)左右像平面X轴重合要求tV,UY=0且tV,UZ=0:可将坐标系OA和OB进行某个旋转让其X轴为tB,A,显然此旋转为X轴叉乘tB,A。由于X轴叉乘tB,A是坐标系OA和OB到坐标系OU和OV的坐标系变换,所以坐标系OA和OB到坐标系OU和OV的点变换为其逆,即tB,A叉乘X轴。于是由叉乘得旋转轴,夹角公式得旋转角,设复合结果为rcross

                      3)为实现在期望的角度重成像,最后还可以添加一个自定义旋转rdelta,于是最终结果为RE=Rdelta*Rcross*RA,RF=Rdelta*Rcross*RB,tE,F=RF*t

             注意:以上即是OpenCV中Radtan针孔成像模型和KB针孔成像模型的实现,只是没有自定义旋转这一步,OpenCV额外模块中的MeiUCM模型还提供了另外一种实现

                      1)为使左右像平面平行,给的解是rA=0和rB=-r,这相对于rA=0.5r和rB=-0.5r的解减少左右的共视区域

                      2)为左右像平面X轴重合,直接让tB,A是新坐标系的X轴,然后让(-tB,AY,tB,AX,0)是Y轴,最后X轴叉乘Y轴得Z轴。

    2.实验测试代码

             依赖于OpenCV、Ceres和Spdlog。

      1 #include <opencv2/opencv.hpp>
      2 #include <ceres/ceres.h>
      3 #include <ceres/rotation.h>
      4 #include <spdlog/spdlog.h>
      5 #include <opencv2/ccalib/omnidir.hpp>
      6 using namespace std;
      7 using namespace cv;
      8 
      9 class StereoRect
     10 {
     11 public:
     12     static void TestMe(int argc = 0, char** argv = 0)
     13     {
     14         for (int k = 0; k < 999; ++k)
     15         {
     16             //0.GenerateData
     17             Mat_<double> R(3, 3); ceres::EulerAnglesToRotationMatrix(Matx31d::randu(-15, 15).val, 0, R.ptr<double>());
     18             Mat_<double> t({ 3, 1 }, { -Matx21d::randu(10, 20)(0), Matx21d::randu(-5, 5)(0), Matx21d::randu(-5, 5)(0) });//left camera x must be negative in right camera system which echoes opencv
     19             Matx31d P = P.randu(-999, 999); if ((P(2) < 0)) P(2) = -P(2);//P from phyCam1 to virCam1 == P from phyCam1 to phyCam2 to virCam2. virCam1 == virCam2 when no translation
     20 
     21             //1.CalcByOpenCVPin
     22             Matx33d R11, R12;
     23             cv::stereoRectify(Matx33d::eye(), Mat(), Matx33d::eye(), Mat(), Size(480, 640), R, t, R11, R12, Matx34d(), Matx34d(), noArray());
     24             double infP11P12 = cv::norm(R11 * P, R12 * R * P, NORM_INF);
     25 
     26             //2.CalcByOpenCVKB
     27             Matx33d R21, R22;
     28             fisheye::stereoRectify(Matx33d::eye(), Matx41d::zeros(), Matx33d::eye(), Matx41d::zeros(), Size(480, 640), R, t, R21, R22, Matx34d(), Matx34d(), noArray(), 0);
     29             double infP21P22 = cv::norm(R21 * P, R22 * R * P, NORM_INF);
     30 
     31             //3.CalcByDIYPin
     32             Matx33d R31, R32;
     33             Matx31d t3 = runRect(R31, R32, R, t);
     34             double infP31P32 = cv::norm(R31 * P, R32 * R * P, NORM_INF);
     35 
     36             //4.CalcByDIYMUCM
     37             Matx33d R41, R42;
     38             runRectTmp(R, t, R41, R42);
     39             double infP41P42 = cv::norm(R41 * P, R42 * R * P, NORM_INF);
     40 
     41             //5.CalcByOpenCVMUCM
     42             Matx33d R51, R52;
     43             omni_stereoRectifyModified(R, t, R51, R52);
     44             double infP51P52 = cv::norm(R51 * P, R52 * R * P, NORM_INF);
     45 
     46             //6.AnalyzeError
     47             double infR11R21 = norm(R11, R21, NORM_INF);
     48             double infR11R31 = norm(R11, R31, NORM_INF);
     49             double infR41R51 = norm(R41, R51, NORM_INF);
     50             double infR12R22 = norm(R12, R22, NORM_INF);
     51             double infR12R32 = norm(R12, R32, NORM_INF);
     52             double infR42R52 = norm(R42, R52, NORM_INF);
     53 
     54             //7.PrintError
     55             cout << endl << "LoopCount: " << k << endl;
     56             if (infP11P12 > 1E-12 || infP21P22 > 1E-12 || infP31P32 > 1E-12 || infP41P42 > 1E-12 || infP51P52 > 1E-12 ||
     57                 infR11R21 > 1E-12 || infR11R31 > 1E-12 || infR41R51 > 1E-12 ||
     58                 infR12R22 > 1E-12 || infR12R32 > 1E-12 || infR42R52 > 1E-12)
     59             {
     60                 cout << endl << "infP11P12: " << infP11P12 << endl;
     61                 cout << endl << "infP21P22: " << infP21P22 << endl;
     62                 cout << endl << "infP31P32: " << infP31P32 << endl;
     63                 cout << endl << "infP41P42: " << infP41P42 << endl;
     64                 cout << endl << "infP51P52: " << infP51P52 << endl;
     65                 cout << endl << "infR11R21: " << infR11R21 << endl;
     66                 cout << endl << "infR11R31: " << infR11R31 << endl;
     67                 cout << endl << "infR11R51: " << infR41R51 << endl;
     68                 cout << endl << "infR12R22: " << infR12R22 << endl;
     69                 cout << endl << "infR12R32: " << infR12R32 << endl;
     70                 cout << endl << "infR12R52: " << infR42R52 << endl;
     71                 if (1)
     72                 {
     73                     cout << endl << "R: " << R << endl << endl;
     74                     cout << endl << "t: " << t << endl << endl;
     75                     cout << endl << "P: " << P << endl << endl;
     76                     //cout << endl << "R11: " << endl << R11 << endl;
     77                     //cout << endl << "R12: " << endl << R12 << endl;
     78                     //cout << endl << "R21: " << endl << R21 << endl;
     79                     //cout << endl << "R22: " << endl << R22 << endl;
     80                     //cout << endl << "R31: " << endl << R31 << endl;
     81                     //cout << endl << "R32: " << endl << R32 << endl;
     82                     //cout << endl << "R41: " << endl << R41 << endl;
     83                     //cout << endl << "R42: " << endl << R42 << endl;
     84                     //cout << endl << "R51: " << endl << R51 << endl;
     85                     //cout << endl << "R52: " << endl << R52 << endl;
     86                 }
     87                 cout << endl << "Press any key to continue" << endl; std::getchar();
     88             }
     89 
     90 
     91         }
     92     }
     93 
     94     static void TestVis(int argc = 0, char** argv = 0)
     95     {
     96 #if 0
     97         //0.GenerateData
     98         auto randomv = [](int min, int max)->int {return rand() % ((max)-(min)+1) + (min); };
     99         Mat_<double> r({ 3, 1 }, { 1, 1, 1 }); r = r / cv::norm(r, NORM_L2) * 3.14 / 180 * randomv(15, 45);
    100         Mat_<double> t({ 3, 1 }, { -double(randomv(10, 20)), double(randomv(2, 5)) , double(randomv(6, 9)) });//left camera x must be negative in right camera system which echoes opencv
    101         Mat_<double> R(3, 3); cv::Rodrigues(r, R);
    102         Matx31d P = P.randu(99, 999);
    103 
    104         //3.CalcByDIYPinKB
    105         Matx33d R31, R32;
    106         Matx31d t3 = runRect(R31, R32, R, t);
    107         double infP31P32 = cv::norm(R31 * P, R32 * R * P, NORM_INF);
    108 
    109         //4.CalcByDIYUCM
    110 
    111         viz::WCoordinateSystem worldCSys(1);
    112         Mat_<cv::Affine3d> camPoses0, camPoses1, camPoses2, camPoses3;
    113         camPoses0.push_back(cv::Affine3d(Matx33d::eye()));//the first or left camera
    114         camPoses0.push_back(cv::Affine3d(R.t()));//the second or right camera
    115         camPoses1.push_back(cv::Affine3d((R11).t(), Vec3d(1, 1, 1)));
    116         camPoses1.push_back(cv::Affine3d((R12 * R).t(), Vec3d(1, 1, 1)));
    117         camPoses2.push_back(cv::Affine3d((R21).t(), Vec3d(2, 2, 2)));
    118         camPoses2.push_back(cv::Affine3d((R22 * R).t(), Vec3d(2, 2, 2)));
    119         camPoses3.push_back(cv::Affine3d((R31).t(), Vec3d(3, 3, 3)));
    120         camPoses3.push_back(cv::Affine3d((R32 * R).t(), Vec3d(3, 3, 3)));
    121 
    122         viz::WTrajectoryFrustums camTraj0(camPoses0, Matx33d(1, 0, 1, 0, 1, 1, 0, 0, 1), 1, viz::Color::white());
    123         viz::WTrajectoryFrustums camTraj1(camPoses1, Matx33d(1, 0, 1, 0, 1, 1, 0, 0, 1), 1, viz::Color::green());
    124         viz::WTrajectoryFrustums camTraj2(camPoses2, Matx33d(1, 0, 1, 0, 1, 1, 0, 0, 1), 1, viz::Color::blue());
    125         viz::WTrajectoryFrustums camTraj3(camPoses3, Matx33d(1, 0, 1, 0, 1, 1, 0, 0, 1), 1, viz::Color::red());
    126 
    127         static viz::Viz3d viz3d(__FUNCTION__);
    128         viz3d.showWidget("worldCSys", worldCSys);
    129         viz3d.showWidget("camTraj", camTraj);
    130         viz3d.spin();
    131 #endif
    132     }
    133 
    134 public:
    135     static Matx31d runRect(Matx33d& R1, Matx33d& R2, Matx33d R, Matx31d t, Vec3d delta = Vec3d(0, 0, 0), double ratio1 = 0.5)
    136     {
    137         //1.Let left and right image plane be coplanar
    138         Matx31d r; cv::Rodrigues(R, r);
    139         Matx33d RCop1; cv::Rodrigues(ratio1 * r, RCop1);
    140         Matx33d RCop2; cv::Rodrigues(ratio1 * r - r, RCop2);
    141         Matx31d tCop = RCop2 * t;
    142 
    143         //2.Let left and right image plane are epipolar
    144         Matx31d xAxis(tCop(0) > 0 ? 1 : -1, 0, 0);
    145         Matx31d rEpi = Vec3d(tCop.val).cross(Vec3d(xAxis.val));
    146         if (norm(rEpi, NORM_L2) > 0.) rEpi *= (acos(fabs(tCop(0)) / norm(tCop, NORM_L2)) / norm(rEpi, NORM_L2));
    147         Matx33d REpi; cv::Rodrigues(rEpi, REpi);
    148 
    149         //3.Additional rotation for reprojection at expectation angle
    150         Matx33d RDelta; cv::Rodrigues(delta, RDelta);
    151 
    152         //4.Final transformation
    153         R1 = RDelta * REpi * RCop1;
    154         R2 = RDelta * REpi * RCop2;
    155         Matx31d tFinal = R2 * t;
    156         return tFinal;
    157     }
    158 
    159     static Matx31d runRectTmp(Matx33d R, Matx31d t, Matx33d& R1, Matx33d& R2, Vec3d delta = Vec3d(0, 0, 0), double ratio1 = 0)
    160     {
    161 #if 1
    162         //1.Let left and right image plane be coplanar
    163         Matx31d r; cv::Rodrigues(R, r);
    164         Matx33d RCop1; cv::Rodrigues(r * ratio1, RCop1);
    165         Matx33d RCop2; cv::Rodrigues(r * (1 - ratio1), RCop2);
    166         Matx31d tCop = -RCop2.t() * t;
    167 
    168         //2.Let left and right image plane are epipolar
    169         Vec3d e1(tCop.val);
    170         e1 = e1 / cv::norm(e1);
    171         Vec3d e2(-e1(1), e1(0), 0);
    172         e2 = e2 / cv::norm(e2);
    173         Vec3d e3 = e1.cross(e2);
    174         e3 = e3 / cv::norm(e3);
    175         Matx33d REpi(e1(0), e1(1), e1(2), e2(0), e2(1), e2(2), e3(0), e3(1), e3(2));
    176 
    177         //3.Additional rotation for reprojection at expectation angle
    178         Matx33d RDelta; cv::Rodrigues(delta, RDelta);
    179 
    180         //4.Final transformation
    181         R1 = RDelta * REpi * RCop1;
    182         R2 = RDelta * REpi * RCop2.t();
    183         Matx31d tFinal = R2 * t;
    184         return tFinal;
    185 #else
    186         //1.Let left and right image plane be coplanar
    187         Matx33d RCop = R.t();
    188         Matx31d tCop = -R.t() * t;//here should not has minus sign and use tCop(0) sign later like cv::stereoRectify and cv::fisheye::stereoRectify
    189 
    190         //2.Let left and right image plane are epipolar
    191         Vec3d e1(tCop.val);
    192         e1 = e1 / cv::norm(e1);
    193         Vec3d e2(-e1(1), e1(0), 0);
    194         e2 = e2 / cv::norm(e2);
    195         Vec3d e3 = e1.cross(e2);
    196         e3 = e3 / cv::norm(e3);
    197         Matx33d REpi(e1(0), e1(1), e1(2), e2(0), e2(1), e2(2), e3(0), e3(1), e3(2));
    198 
    199         //3.Additional rotation for reprojection at expectation angle
    200         Matx33d RDelta; cv::Rodrigues(delta, RDelta);
    201 
    202         //4.Final transformation
    203         R1 = RDelta * REpi;
    204         R2 = RDelta * REpi * RCop;
    205         Matx31d tFinal = R2 * t;
    206         return tFinal;
    207 #endif
    208     }
    209 
    210     static void omni_stereoRectifyModified(InputArray R, InputArray T, OutputArray R1, OutputArray R2)
    211     {
    212         CV_Assert((R.size() == Size(3, 3) || R.total() == 3) && (R.depth() == CV_32F || R.depth() == CV_64F));
    213         CV_Assert(T.total() == 3 && (T.depth() == CV_32F || T.depth() == CV_64F));
    214 
    215         Mat _R, _T;
    216         if (R.size() == Size(3, 3))
    217         {
    218             R.getMat().convertTo(_R, CV_64F);
    219         }
    220         else if (R.total() == 3)
    221         {
    222             Rodrigues(R.getMat(), _R);
    223             _R.convertTo(_R, CV_64F);
    224         }
    225 
    226         T.getMat().reshape(1, 3).convertTo(_T, CV_64F);
    227 
    228         R1.create(3, 3, CV_64F);
    229         Mat _R1 = R1.getMat();
    230         R2.create(3, 3, CV_64F);
    231         Mat _R2 = R2.getMat();
    232 
    233         Mat R21 = _R.t();
    234         Mat T21 = -_R.t() * _T;
    235 
    236         Mat e1, e2, e3;
    237         e1 = T21.t() / norm(T21);
    238         e2 = Mat(Matx13d(T21.at<double>(1) * -1, T21.at<double>(0), 0.0));
    239         e2 = e2 / norm(e2);
    240         e3 = e1.cross(e2);
    241         e3 = e3 / norm(e3);
    242         e1.copyTo(_R1.row(0));
    243         e2.copyTo(_R1.row(1));
    244         e3.copyTo(_R1.row(2));
    245         _R2 = _R1 * R21;
    246     }
    247 };
    248 
    249 int main(int argc, char** argv) { StereoRect::TestMe(argc, argv); return 0; }
    250 int main1(int argc, char** argv) { StereoRect::TestMe(argc, argv); return 0; }
    251 int main2(int argc, char** argv) { StereoRect::TestVis(argc, argv); return 0; }
    View Code
  • 相关阅读:
    pip不是内部或外部命令也不是可运行的程序或批处理文件的问题
    动态规划 leetcode 343,279,91 & 639. Decode Ways,62,63,198
    动态规划 70.climbing Stairs ,120,64
    (双指针+链表) leetcode 19. Remove Nth Node from End of List,61. Rotate List,143. Reorder List,234. Palindrome Linked List
    建立链表的虚拟头结点 203 Remove Linked List Element,82,147,148,237
    链表 206 Reverse Linked List, 92,86, 328, 2, 445
    (数组,哈希表) 219.Contains Duplicate(2),217 Contain Duplicate, 220(3)
    重装系统
    java常用IO
    端口
  • 原文地址:https://www.cnblogs.com/dzyBK/p/14136322.html
Copyright © 2011-2022 走看看