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
原文地址:https://www.cnblogs.com/dzyBK/p/14136322.html