疑点解答:

摄像机矩阵由内参矩阵和外参矩阵组成,对摄像机矩阵进行QR分解可以得到内参矩阵和外参矩阵。

内参包括焦距、主点、倾斜系数、畸变系数

(1)

其中,fx,fy为焦距,一般情况下,二者相等,x0、y0为主点坐标(相对于成像平面),s为坐标轴倾斜参数,理想情况下为0

外参包括旋转矩阵R3×3、平移向量T3×1,它们共同描述了如何把点从世界坐标系转换到摄像机坐标系,旋转矩阵描述了世界坐标系的坐标轴相对于摄像机坐标轴的方向,平移向量描述了在摄像机坐标系下空间原点的位置。

转::标定双目后,首先要根据其畸变系数来校正原图,可以参考

http://blog.csdn.net/qq_15947787/article/details/51471535

[cpp] view plain copy

  1. Mat jiaozheng( Mat image )
  2. {
  3. Size image_size = image.size();
  4. float intrinsic[3][3] = {589.2526583947847,0,321.8607532099886,0,585.7784771038199,251.0338528599469,0,0,1};
  5. float distortion[1][5] = {-0.5284205687061442, 0.3373615384253201, -0.002133029981628697, 0.001511983002864886, -0.1598661778309496};
  6. Mat intrinsic_matrix = Mat(3,3,CV_32FC1,intrinsic);
  7. Mat distortion_coeffs = Mat(1,5,CV_32FC1,distortion);
  8. Mat R = Mat::eye(3,3,CV_32F);
  9. Mat mapx = Mat(image_size,CV_32FC1);
  10. Mat mapy = Mat(image_size,CV_32FC1);
  11. initUndistortRectifyMap(intrinsic_matrix,distortion_coeffs,R,intrinsic_matrix,image_size,CV_32FC1,mapx,mapy);
  12. Mat t = image.clone();
  13. cv::remap( image, t, mapx, mapy, INTER_LINEAR);
  14. return t;
  15. }

校正完成后就可以进行坐标计算了,分两种

(1)世界坐标系——>像面坐标系

首先将世界坐标系——>摄像机坐标系

已知某点在世界坐标系中的坐标为(Xw, Yw, Zw),由旋转和平移矩阵可得摄像机坐标系和世界坐标系的关系为

(2)

然后将摄像机坐标系——>像面坐标系

(3)

其中[u v 1]T为点在图像坐标系中的坐标,[Xc Yc  Zc  1]T为点在摄像机坐标系中的坐标,K为摄像机内参数矩阵。

这样最终可以得到:

(4)

(2)像面坐标系——>世界坐标系

光轴会聚模型:

对于两相机分别有:

(5)          (6)

公式56,左边Z应分别为Zc1,Zc2

其中,

(7)

这样可以把(5)(6)写成

(8)

公式8左边Z应为Zc1

(9)

公式9左边Z应为Zc2

将(8)(9)整理可以得到

(10)

以上公式参考评论6楼 wisemanjack

采用最小二乘法求解X,Y,Z,在opencv中可以用solve(A,B,XYZ,DECOMP_SVD)求解

代码如下:

[cpp] view plain copy

  1. //opencv2.4.9 vs2012
  2. #include <opencv2\opencv.hpp>
  3. #include <fstream>
  4. #include <iostream>
  5. using namespace std;
  6. using namespace cv;
  7. Point2f xyz2uv(Point3f worldPoint,float intrinsic[3][3],float translation[1][3],float rotation[3][3]);
  8. Point3f uv2xyz(Point2f uvLeft,Point2f uvRight);
  9. //图片对数量
  10. int PicNum = 14;
  11. //左相机内参数矩阵
  12. float leftIntrinsic[3][3] = {4037.82450,             0,     947.65449,
  13. 0,    3969.79038,     455.48718,
  14. 0,             0,             1};
  15. //左相机畸变系数
  16. float leftDistortion[1][5] = {0.18962, -4.05566, -0.00510, 0.02895, 0};
  17. //左相机旋转矩阵
  18. float leftRotation[3][3] = {0.912333,       -0.211508,       0.350590,
  19. 0.023249,       -0.828105,      -0.560091,
  20. 0.408789,        0.519140,      -0.750590};
  21. //左相机平移向量
  22. float leftTranslation[1][3] = {-127.199992, 28.190639, 1471.356768};
  23. //右相机内参数矩阵
  24. float rightIntrinsic[3][3] = {3765.83307,            0,     339.31958,
  25. 0,  3808.08469,     660.05543,
  26. 0,           0,             1};
  27. //右相机畸变系数
  28. float rightDistortion[1][5] = {-0.24195, 5.97763, -0.02057, -0.01429, 0};
  29. //右相机旋转矩阵
  30. float rightRotation[3][3] = {-0.134947,      0.989568,      -0.050442,
  31. 0.752355,      0.069205,      -0.655113,
  32. -0.644788,     -0.126356,      -0.753845};
  33. //右相机平移向量
  34. float rightTranslation[1][3] = {50.877397, -99.796492, 1507.312197};
  35. int main()
  36. {
  37. //已知空间坐标求成像坐标
  38. Point3f point(700,220,530);
  39. cout<<"左相机中坐标:"<<endl;
  40. cout<<xyz2uv(point,leftIntrinsic,leftTranslation,leftRotation)<<endl;
  41. cout<<"右相机中坐标:"<<endl;
  42. cout<<xyz2uv(point,rightIntrinsic,rightTranslation,rightRotation)<<endl;
  43. //已知左右相机成像坐标求空间坐标
  44. Point2f l = xyz2uv(point,leftIntrinsic,leftTranslation,leftRotation);
  45. Point2f r = xyz2uv(point,rightIntrinsic,rightTranslation,rightRotation);
  46. Point3f worldPoint;
  47. worldPoint = uv2xyz(l,r);
  48. cout<<"空间坐标为:"<<endl<<uv2xyz(l,r)<<endl;
  49. system("pause");
  50. return 0;
  51. }
  52. //************************************
  53. // Description: 根据左右相机中成像坐标求解空间坐标
  54. // Method:    uv2xyz
  55. // FullName:  uv2xyz
  56. // Access:    public
  57. // Parameter: Point2f uvLeft
  58. // Parameter: Point2f uvRight
  59. // Returns:   cv::Point3f
  60. // Author:    小白
  61. // Date:      2017/01/10
  62. // History:
  63. //************************************
  64. Point3f uv2xyz(Point2f uvLeft,Point2f uvRight)
  65. {
  66. //  [u1]      |X|                     [u2]      |X|
  67. //Z*|v1| = Ml*|Y|                   Z*|v2| = Mr*|Y|
  68. //  [ 1]      |Z|                     [ 1]      |Z|
  69. //            |1|                               |1|
  70. Mat mLeftRotation = Mat(3,3,CV_32F,leftRotation);
  71. Mat mLeftTranslation = Mat(3,1,CV_32F,leftTranslation);
  72. Mat mLeftRT = Mat(3,4,CV_32F);//左相机M矩阵
  73. hconcat(mLeftRotation,mLeftTranslation,mLeftRT);
  74. Mat mLeftIntrinsic = Mat(3,3,CV_32F,leftIntrinsic);
  75. Mat mLeftM = mLeftIntrinsic * mLeftRT;
  76. //cout<<"左相机M矩阵 = "<<endl<<mLeftM<<endl;
  77. Mat mRightRotation = Mat(3,3,CV_32F,rightRotation);
  78. Mat mRightTranslation = Mat(3,1,CV_32F,rightTranslation);
  79. Mat mRightRT = Mat(3,4,CV_32F);//右相机M矩阵
  80. hconcat(mRightRotation,mRightTranslation,mRightRT);
  81. Mat mRightIntrinsic = Mat(3,3,CV_32F,rightIntrinsic);
  82. Mat mRightM = mRightIntrinsic * mRightRT;
  83. //cout<<"右相机M矩阵 = "<<endl<<mRightM<<endl;
  84. //最小二乘法A矩阵
  85. Mat A = Mat(4,3,CV_32F);
  86. A.at<float>(0,0) = uvLeft.x * mLeftM.at<float>(2,0) - mLeftM.at<float>(0,0);
  87. A.at<float>(0,1) = uvLeft.x * mLeftM.at<float>(2,1) - mLeftM.at<float>(0,1);
  88. A.at<float>(0,2) = uvLeft.x * mLeftM.at<float>(2,2) - mLeftM.at<float>(0,2);
  89. A.at<float>(1,0) = uvLeft.y * mLeftM.at<float>(2,0) - mLeftM.at<float>(1,0);
  90. A.at<float>(1,1) = uvLeft.y * mLeftM.at<float>(2,1) - mLeftM.at<float>(1,1);
  91. A.at<float>(1,2) = uvLeft.y * mLeftM.at<float>(2,2) - mLeftM.at<float>(1,2);
  92. A.at<float>(2,0) = uvRight.x * mRightM.at<float>(2,0) - mRightM.at<float>(0,0);
  93. A.at<float>(2,1) = uvRight.x * mRightM.at<float>(2,1) - mRightM.at<float>(0,1);
  94. A.at<float>(2,2) = uvRight.x * mRightM.at<float>(2,2) - mRightM.at<float>(0,2);
  95. A.at<float>(3,0) = uvRight.y * mRightM.at<float>(2,0) - mRightM.at<float>(1,0);
  96. A.at<float>(3,1) = uvRight.y * mRightM.at<float>(2,1) - mRightM.at<float>(1,1);
  97. A.at<float>(3,2) = uvRight.y * mRightM.at<float>(2,2) - mRightM.at<float>(1,2);
  98. //最小二乘法B矩阵
  99. Mat B = Mat(4,1,CV_32F);
  100. B.at<float>(0,0) = mLeftM.at<float>(0,3) - uvLeft.x * mLeftM.at<float>(2,3);
  101. B.at<float>(1,0) = mLeftM.at<float>(1,3) - uvLeft.y * mLeftM.at<float>(2,3);
  102. B.at<float>(2,0) = mRightM.at<float>(0,3) - uvRight.x * mRightM.at<float>(2,3);
  103. B.at<float>(3,0) = mRightM.at<float>(1,3) - uvRight.y * mRightM.at<float>(2,3);
  104. Mat XYZ = Mat(3,1,CV_32F);
  105. //采用SVD最小二乘法求解XYZ
  106. solve(A,B,XYZ,DECOMP_SVD);
  107. //cout<<"空间坐标为 = "<<endl<<XYZ<<endl;
  108. //世界坐标系中坐标
  109. Point3f world;
  110. world.x = XYZ.at<float>(0,0);
  111. world.y = XYZ.at<float>(1,0);
  112. world.z = XYZ.at<float>(2,0);
  113. return world;
  114. }
  115. //************************************
  116. // Description: 将世界坐标系中的点投影到左右相机成像坐标系中
  117. // Method:    xyz2uv
  118. // FullName:  xyz2uv
  119. // Access:    public
  120. // Parameter: Point3f worldPoint
  121. // Parameter: float intrinsic[3][3]
  122. // Parameter: float translation[1][3]
  123. // Parameter: float rotation[3][3]
  124. // Returns:   cv::Point2f
  125. // Author:    小白
  126. // Date:      2017/01/10
  127. // History:
  128. //************************************
  129. Point2f xyz2uv(Point3f worldPoint,float intrinsic[3][3],float translation[1][3],float rotation[3][3])
  130. {
  131. //    [fx s x0]                         [Xc]        [Xw]        [u]   1     [Xc]
  132. //K = |0 fy y0|       TEMP = [R T]      |Yc| = TEMP*|Yw|        | | = —*K *|Yc|
  133. //    [ 0 0 1 ]                         [Zc]        |Zw|        [v]   Zc    [Zc]
  134. //                                                  [1 ]
  135. Point3f c;
  136. c.x = rotation[0][0]*worldPoint.x + rotation[0][1]*worldPoint.y + rotation[0][2]*worldPoint.z + translation[0][0]*1;
  137. c.y = rotation[1][0]*worldPoint.x + rotation[1][1]*worldPoint.y + rotation[1][2]*worldPoint.z + translation[0][1]*1;
  138. c.z = rotation[2][0]*worldPoint.x + rotation[2][1]*worldPoint.y + rotation[2][2]*worldPoint.z + translation[0][2]*1;
  139. Point2f uv;
  140. uv.x = (intrinsic[0][0]*c.x + intrinsic[0][1]*c.y + intrinsic[0][2]*c.z)/c.z;
  141. uv.y = (intrinsic[1][0]*c.x + intrinsic[1][1]*c.y + intrinsic[1][2]*c.z)/c.z;
  142. return uv;
  143. }

2017/5/26补充

matlab或者opencv标定完都是在左相机上建立世界坐标系,于是上面代码对应的改为:

[cpp] view plain copy

  1. //左相机旋转矩阵
  2. float leftRotation[3][3] = {1,0,0,  0,1,0,  0,0,1 };
  3. //左相机平移向量
  4. float leftTranslation[1][3] = {0,0,0};

畸变矩阵(默认获得5个即便参数k1,k2,p1,p2,k3)

——————————————————————————————————————————————————————————————

像面坐标系——>世界坐标系还有一种模型是光轴平行模型

双目立体视觉三位测量是基于视差原理:

(11)

(12)

这里,除cx‘外的所有参数都来自于左图像,cx’是主点在右图像上的x坐标。如果主光线在无穷远处相交,那么cx=cx‘,并且右下角的项为0,给定一个二维齐次点和其关联的视差d,我们可以将此点投影到三维中:

(13)

三维坐标就是(X / W , Y / W , Z / W)

光轴平行模型要得到视差图,http://blog.csdn.net/wangchao7281/article/details/52506691?locationNum=7

可以参考opencv的例子,例子的使用方法可以参考http://blog.csdn.net/t247555529/article/details/48046859

得到视差图后可以调用cvReprojectImageTo3D输出的三维坐标

看到很多人输出三维坐标时z出现10000,那个其实是输出方式不对,应该是下面这样

[cpp] view plain copy

  1. Point p;
  2. p.x =294,p.y=189;
  3. cout<<p<< "in world coordinate: " << xyz.at<Vec3f>(p)*16 <<endl;

为什么要乘以16呢?

因为在OpenCV2.0中,BM函数得出的结果是以16位符号数的形式的存储的,出于精度需要,所有的视差在输出时都扩大了16倍(2^4)。其具体代码表示如下:

[cpp] view plain copy

  1. dptr[y*dstep] = (short)(((ndisp - mind - 1 + mindisp)*256 + (d != 0 ? (p-n)*128/d : 0) + 15) >> 4);

可以看到,原始视差在左移8位(256)并且加上一个修正值之后又右移了4位,最终的结果就是左移4位

因此,在实际求距离时,cvReprojectTo3D出来的X/W,Y/W,Z/W都要乘以16 (也就是W除以16),才能得到正确的三维坐标信息

下面是在opencv3.0下实现的该方法的测距(转载)

[cpp] view plain copy

  1. /******************************/
  2. /*        立体匹配和测距        */
  3. /******************************/
  4. #include <opencv2/opencv.hpp>
  5. #include <iostream>
  6. using namespace std;
  7. using namespace cv;
  8. const int imageWidth = 640;                             //摄像头的分辨率
  9. const int imageHeight = 480;
  10. Size imageSize = Size(imageWidth, imageHeight);
  11. Mat rgbImageL, grayImageL;
  12. Mat rgbImageR, grayImageR;
  13. Mat rectifyImageL, rectifyImageR;
  14. Rect validROIL;//图像校正之后,会对图像进行裁剪,这里的validROI就是指裁剪之后的区域
  15. Rect validROIR;
  16. Mat mapLx, mapLy, mapRx, mapRy;     //映射表
  17. Mat Rl, Rr, Pl, Pr, Q;              //校正旋转矩阵R,投影矩阵P 重投影矩阵Q
  18. Mat xyz;              //三维坐标
  19. Point origin;         //鼠标按下的起始点
  20. Rect selection;      //定义矩形选框
  21. bool selectObject = false;    //是否选择对象
  22. int blockSize = 0, uniquenessRatio =0, numDisparities=0;
  23. Ptr<StereoBM> bm = StereoBM::create(16, 9);
  24. /*
  25. 事先标定好的相机的参数
  26. fx 0 cx
  27. 0 fy cy
  28. 0 0  1
  29. */
  30. Mat cameraMatrixL = (Mat_<double>(3, 3) << 836.771593170594,0,319.970748854743,
  31. 0,839.416501863912,228.788913693256,
  32. 0, 0, 1);
  33. Mat distCoeffL = (Mat_<double>(5, 1) << 0, 0, 0, 0, 0);
  34. Mat cameraMatrixR = (Mat_<double>(3, 3) << 838.101721655709,0,319.647150557935,
  35. 0,840.636812165056,250.655818405938,
  36. 0, 0, 1);
  37. Mat distCoeffR = (Mat_<double>(5, 1) << 0, 0, 0, 0, 0);
  38. Mat T = (Mat_<double>(3, 1) << -39.7389449993974,0.0740619639178984,0.411914303245886);//T平移向量
  39. Mat rec = (Mat_<double>(3, 1) << -0.00306, -0.03207, 0.00206);//rec旋转向量
  40. Mat R = (Mat_<double>(3, 3) << 0.999957725513956,-0.00103511880221423,0.00913650447492805,
  41. 0.00114462826834523,0.999927476064641,-0.0119888463633882,
  42. -0.00912343197938050,0.0119987974423658,0.999886389470751);//R 旋转矩阵
  43. /*****立体匹配*****/
  44. void stereo_match(int,void*)
  45. {
  46. bm->setBlockSize(2*blockSize+5);     //SAD窗口大小,5~21之间为宜
  47. bm->setROI1(validROIL);
  48. bm->setROI2(validROIR);
  49. bm->setPreFilterCap(31);
  50. bm->setMinDisparity(0);  //最小视差,默认值为0, 可以是负值,int型
  51. bm->setNumDisparities(numDisparities*16+16);//视差窗口,即最大视差值与最小视差值之差,窗口大小必须是16的整数倍,int型
  52. bm->setTextureThreshold(10);
  53. bm->setUniquenessRatio(uniquenessRatio);//uniquenessRatio主要可以防止误匹配
  54. bm->setSpeckleWindowSize(100);
  55. bm->setSpeckleRange(32);
  56. bm->setDisp12MaxDiff(-1);
  57. Mat disp, disp8;
  58. bm->compute(rectifyImageL, rectifyImageR, disp);//输入图像必须为灰度图
  59. disp.convertTo(disp8, CV_8U, 255 / ((numDisparities * 16 + 16)*16.));//计算出的视差是CV_16S格式
  60. reprojectImageTo3D(disp, xyz, Q, true); //在实际求距离时,ReprojectTo3D出来的X / W, Y / W, Z / W都要乘以16(也就是W除以16),才能得到正确的三维坐标信息。
  61. xyz = xyz * 16;
  62. imshow("disparity", disp8);
  63. }
  64. /*****描述:鼠标操作回调*****/
  65. static void onMouse(int event, int x, int y, int, void*)
  66. {
  67. if (selectObject)
  68. {
  69. selection.x = MIN(x, origin.x);
  70. selection.y = MIN(y, origin.y);
  71. selection.width = std::abs(x - origin.x);
  72. selection.height = std::abs(y - origin.y);
  73. }
  74. switch (event)
  75. {
  76. case EVENT_LBUTTONDOWN:   //鼠标左按钮按下的事件
  77. origin = Point(x, y);
  78. selection = Rect(x, y, 0, 0);
  79. selectObject = true;
  80. cout << origin <<"in world coordinate is: " << xyz.at<Vec3f>(origin) << endl;
  81. break;
  82. case EVENT_LBUTTONUP:    //鼠标左按钮释放的事件
  83. selectObject = false;
  84. if (selection.width > 0 && selection.height > 0)
  85. break;
  86. }
  87. }
  88. /*****主函数*****/
  89. int main()
  90. {
  91. /*
  92. 立体校正
  93. */
  94. //Rodrigues(rec, R); //Rodrigues变换
  95. stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR, imageSize, R, T, Rl, Rr, Pl, Pr, Q, CALIB_ZERO_DISPARITY,
  96. 0, imageSize, &validROIL, &validROIR);
  97. initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pr, imageSize, CV_32FC1, mapLx, mapLy);
  98. initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr, imageSize, CV_32FC1, mapRx, mapRy);
  99. /*
  100. 读取图片
  101. */
  102. rgbImageL = imread("左2.jpg", CV_LOAD_IMAGE_COLOR);
  103. cvtColor(rgbImageL, grayImageL, CV_BGR2GRAY);
  104. rgbImageR = imread("右2.jpg", CV_LOAD_IMAGE_COLOR);
  105. cvtColor(rgbImageR, grayImageR, CV_BGR2GRAY);
  106. imshow("ImageL Before Rectify", grayImageL);
  107. imshow("ImageR Before Rectify", grayImageR);
  108. /*
  109. 经过remap之后,左右相机的图像已经共面并且行对准了
  110. */
  111. remap(grayImageL, rectifyImageL, mapLx, mapLy, INTER_LINEAR);
  112. remap(grayImageR, rectifyImageR, mapRx, mapRy, INTER_LINEAR);
  113. /*
  114. 把校正结果显示出来
  115. */
  116. Mat rgbRectifyImageL, rgbRectifyImageR;
  117. cvtColor(rectifyImageL, rgbRectifyImageL, CV_GRAY2BGR);  //伪彩色图
  118. cvtColor(rectifyImageR, rgbRectifyImageR, CV_GRAY2BGR);
  119. //单独显示
  120. //rectangle(rgbRectifyImageL, validROIL, Scalar(0, 0, 255), 3, 8);
  121. //rectangle(rgbRectifyImageR, validROIR, Scalar(0, 0, 255), 3, 8);
  122. imshow("ImageL After Rectify", rgbRectifyImageL);
  123. imshow("ImageR After Rectify", rgbRectifyImageR);
  124. //显示在同一张图上
  125. Mat canvas;
  126. double sf;
  127. int w, h;
  128. sf = 600. / MAX(imageSize.width, imageSize.height);
  129. w = cvRound(imageSize.width * sf);
  130. h = cvRound(imageSize.height * sf);
  131. canvas.create(h, w * 2, CV_8UC3);   //注意通道
  132. //左图像画到画布上
  133. Mat canvasPart = canvas(Rect(w * 0, 0, w, h));                                //得到画布的一部分
  134. resize(rgbRectifyImageL, canvasPart, canvasPart.size(), 0, 0, INTER_AREA);     //把图像缩放到跟canvasPart一样大小
  135. Rect vroiL(cvRound(validROIL.x*sf), cvRound(validROIL.y*sf),                //获得被截取的区域
  136. cvRound(validROIL.width*sf), cvRound(validROIL.height*sf));
  137. //rectangle(canvasPart, vroiL, Scalar(0, 0, 255), 3, 8);                      //画上一个矩形
  138. cout << "Painted ImageL" << endl;
  139. //右图像画到画布上
  140. canvasPart = canvas(Rect(w, 0, w, h));                                      //获得画布的另一部分
  141. resize(rgbRectifyImageR, canvasPart, canvasPart.size(), 0, 0, INTER_LINEAR);
  142. Rect vroiR(cvRound(validROIR.x * sf), cvRound(validROIR.y*sf),
  143. cvRound(validROIR.width * sf), cvRound(validROIR.height * sf));
  144. //rectangle(canvasPart, vroiR, Scalar(0, 0, 255), 3, 8);
  145. cout << "Painted ImageR" << endl;
  146. //画上对应的线条
  147. for (int i = 0; i < canvas.rows; i += 16)
  148. line(canvas, Point(0, i), Point(canvas.cols, i), Scalar(0, 255, 0), 1, 8);
  149. imshow("rectified", canvas);
  150. /*
  151. 立体匹配
  152. */
  153. namedWindow("disparity", CV_WINDOW_AUTOSIZE);
  154. // 创建SAD窗口 Trackbar
  155. createTrackbar("BlockSize:\n", "disparity",&blockSize, 8, stereo_match);
  156. // 创建视差唯一性百分比窗口 Trackbar
  157. createTrackbar("UniquenessRatio:\n", "disparity", &uniquenessRatio, 50, stereo_match);
  158. // 创建视差窗口 Trackbar
  159. createTrackbar("NumDisparities:\n", "disparity", &numDisparities, 16, stereo_match);
  160. //鼠标响应函数setMouseCallback(窗口名称, 鼠标回调函数, 传给回调函数的参数,一般取0)
  161. setMouseCallback("disparity", onMouse, 0);
  162. stereo_match(0,0);
  163. waitKey(0);
  164. return 0;
  165. }

————————————————————————————————————————

最后是完整的大作业代码,感觉自己写的并不是很好,敷衍了事~~

运行该程序,会对素材文件夹中图像进行处理,生成三个文件夹,分别是“大球圆心”、“畸变校正”、“亮度对比度”以及一个csv文件,文件名为“三维坐标.csv”,里面记录了计算得到的左右相机中球的像面坐标和解算出的空间坐标。该程序默认对所有计算出的圆心坐标进行了重新赋值,如需修改圆心坐标,需在工程中的initPos()修改赋值语句,如需程序自动计算,在主函数中注释该指令即可,但是自动计算的圆心并不准确。

[cpp] view plain copy

  1. //opencv2.4.9 vs2012
  2. #include <opencv2\opencv.hpp>
  3. #include <fstream>
  4. #include <iostream>
  5. using namespace std;
  6. using namespace cv;
  7. //图片对数量
  8. #define  PicNum  14
  9. //左相机内参数矩阵
  10. float leftIntrinsic[3][3] = {4037.82450,             0,     947.65449,
  11. 0,    3969.79038,     455.48718,
  12. 0,             0,             1};
  13. //左相机畸变系数
  14. float leftDistortion[1][5] = {0.18962, -4.05566, -0.00510, 0.02895, 0};
  15. //左相机旋转矩阵
  16. float leftRotation[3][3] = {0.912333,       -0.211508,       0.350590,
  17. 0.023249,       -0.828105,      -0.560091,
  18. 0.408789,        0.519140,      -0.750590};
  19. //左相机平移向量
  20. float leftTranslation[1][3] = {-127.199992, 28.190639, 1471.356768};
  21. //右相机内参数矩阵
  22. float rightIntrinsic[3][3] = {3765.83307,            0,     339.31958,
  23. 0,  3808.08469,     660.05543,
  24. 0,           0,             1};
  25. //右相机畸变系数
  26. float rightDistortion[1][5] = {-0.24195, 5.97763, -0.02057, -0.01429, 0};
  27. //右相机旋转矩阵
  28. float rightRotation[3][3] = {-0.134947,      0.989568,      -0.050442,
  29. 0.752355,      0.069205,      -0.655113,
  30. -0.644788,     -0.126356,      -0.753845};
  31. //右相机平移向量
  32. float rightTranslation[1][3] = {50.877397, -99.796492, 1507.312197};
  33. //球坐标数组
  34. //大球
  35. float rightDaqiu[PicNum][2] = {0};
  36. float leftDaqiu[PicNum][2] = {0};
  37. float worldDaqiu[PicNum][3] = {0};
  38. //小球
  39. float rightXiaoqiu[PicNum][2] = {0};
  40. float leftXiaoqiu[PicNum][2] = {0};
  41. float worldXiaoqiu[PicNum][3] = {0};
  42. //花球
  43. float rightHuaqiu[PicNum][2] = {0};
  44. float leftHuaqiu[PicNum][2] = {0};
  45. float worldHuaqiu[PicNum][3] = {0};
  46. void ContrastAndBright(double alpha, double beta);//调节亮度/对比度
  47. void CorrectionProcess();//对素材校正畸变
  48. void initPos();//手动赋值球的图像坐标
  49. void Daqiu();//计算大球图像坐标
  50. Mat PictureCorrection( Mat image ,float intrinsic[3][3],float distortion[1][5]);//单张图像畸变校正
  51. Point2f xyz2uv(Point3f worldPoint,float intrinsic[3][3],float translation[1][3],float rotation[3][3]);//从世界坐标转为图像坐标
  52. Point3f uv2xyz(Point2f uvLeft,Point2f uvRight);//从图像坐标转为世界坐标
  53. int main()
  54. {
  55. CorrectionProcess();//对素材校正畸变
  56. ContrastAndBright(2.5,50);//调节亮度/对比度
  57. Daqiu();//自动计算大球坐标
  58. initPos();//手动修正,如需验证数据,可以在该函数中修改
  59. //求取大球的空间坐标
  60. cout<<"求解大球的世界坐标:"<<endl;
  61. for (int i=0; i<PicNum; i++)
  62. {
  63. Point2f l,r;
  64. Point3f worldPoint;
  65. l.x = leftDaqiu[i][0];
  66. l.y = leftDaqiu[i][1];
  67. r.x = rightDaqiu[i][0];
  68. r.y = rightDaqiu[i][1];
  69. worldPoint = uv2xyz(l,r);
  70. cout<< worldPoint <<endl;
  71. worldDaqiu[i][0] = worldPoint.x;
  72. worldDaqiu[i][1] = worldPoint.y;
  73. worldDaqiu[i][2] = worldPoint.z;
  74. }
  75. cout<<"求解小球的世界坐标:"<<endl;
  76. for (int i=0; i<PicNum; i++)
  77. {
  78. Point2f l,r;
  79. Point3f worldPoint;
  80. l.x = leftXiaoqiu[i][0];
  81. l.y = leftXiaoqiu[i][1];
  82. r.x = rightXiaoqiu[i][0];
  83. r.y = rightXiaoqiu[i][1];
  84. worldPoint = uv2xyz(l,r);
  85. cout<< worldPoint <<endl;
  86. worldXiaoqiu[i][0] = worldPoint.x;
  87. worldXiaoqiu[i][1] = worldPoint.y;
  88. worldXiaoqiu[i][2] = worldPoint.z;
  89. }
  90. cout<<"求解花球的世界坐标:"<<endl;
  91. for (int i=0; i<PicNum; i++)
  92. {
  93. Point2f l,r;
  94. Point3f worldPoint;
  95. l.x = leftHuaqiu[i][0];
  96. l.y = leftHuaqiu[i][1];
  97. r.x = rightHuaqiu[i][0];
  98. r.y = rightHuaqiu[i][1];
  99. worldPoint = uv2xyz(l,r);
  100. cout<< worldPoint <<endl;
  101. worldHuaqiu[i][0] = worldPoint.x;
  102. worldHuaqiu[i][1] = worldPoint.y;
  103. worldHuaqiu[i][2] = worldPoint.z;
  104. }
  105. //csv文件写入部分
  106. ofstream oFile;  //定义文件输出流
  107. oFile.open("三维坐标.csv", ios::out | ios::trunc);    //打开要输出的文件     这样就很容易的输出一个需要的excel 文件
  108. //写入大球数据
  109. oFile << "大球坐标" << endl;
  110. oFile << "左相机坐标,,,右相机坐标,,,世界坐标" << endl;
  111. oFile << "x,y,,x,y,,x,y,z" << endl;
  112. for (int i=0; i<PicNum ;i++)
  113. {
  114. oFile << leftDaqiu[i][0] <<","<< leftDaqiu[i][1] << ",," << rightDaqiu[i][0] <<","<< rightDaqiu[i][1]
  115. << ",," << worldDaqiu[i][0] <<","<<  worldDaqiu[i][1] <<","<<  worldDaqiu[i][2] << endl;
  116. }
  117. //写入小球数据
  118. oFile << "小球坐标" << endl;
  119. oFile << "左相机坐标,,,右相机坐标,,,世界坐标" << endl;
  120. oFile << "x,y,,x,y,,x,y,z" << endl;
  121. for (int i=0; i<PicNum ;i++)
  122. {
  123. oFile << leftXiaoqiu[i][0] <<","<< leftXiaoqiu[i][1] << ",," << rightXiaoqiu[i][0] <<","<< rightXiaoqiu[i][1]
  124. << ",," << worldXiaoqiu[i][0] <<","<<  worldXiaoqiu[i][1] <<","<<  worldXiaoqiu[i][2] << endl;
  125. }
  126. //写入花球数据
  127. oFile << "花球坐标" << endl;
  128. oFile << "左相机坐标,,,右相机坐标,,,世界坐标" << endl;
  129. oFile << "x,y,,x,y,,x,y,z" << endl;
  130. for (int i=0; i<PicNum ;i++)
  131. {
  132. oFile << leftHuaqiu[i][0] <<","<< leftHuaqiu[i][1] << ",," << rightHuaqiu[i][0] <<","<< rightHuaqiu[i][1]
  133. << ",," << worldHuaqiu[i][0] <<","<<  worldHuaqiu[i][1] <<","<<  worldHuaqiu[i][2] << endl;
  134. }
  135. //关闭文件
  136. oFile.close();
  137. //test
  138. 已知空间坐标求成像坐标
  139. //Point3f point(700,220,530);
  140. //cout<<"左相机中坐标:"<<endl;
  141. //cout<<xyz2uv(point,leftIntrinsic,leftTranslation,leftRotation)<<endl;
  142. //cout<<"右相机中坐标:"<<endl;
  143. //cout<<xyz2uv(point,rightIntrinsic,rightTranslation,rightRotation)<<endl;
  144. //已知左右相机成像坐标求空间坐标
  145. //Point2f l = xyz2uv(point,leftIntrinsic,leftTranslation,leftRotation);
  146. //Point2f r = xyz2uv(point,rightIntrinsic,rightTranslation,rightRotation);
  147. //Point3f worldPoint;
  148. //worldPoint = uv2xyz(l,r);
  149. //cout<<"空间坐标为:"<<endl<<uv2xyz(l,r)<<endl;
  150. /*
  151. //csv文件读取部分
  152. string value;//临时字符串
  153. ifstream iFile("三维坐标.csv");//打开要读入的文件
  154. //循环行读取
  155. while (iFile.good())
  156. {
  157. getline(iFile,value);
  158. //getline(iFile,value,','); //.csv文件用","作为分隔符
  159. cout<<value<<endl;
  160. }
  161. */
  162. system("pause");
  163. return 0;
  164. }
  165. //************************************
  166. // Description: 修正圆心坐标
  167. // Method:    initPos
  168. // FullName:  initPos
  169. // Access:    public
  170. // Returns:   void
  171. // Author:    bhy
  172. // Date:      2016/12/25
  173. // History:
  174. //************************************
  175. void initPos()
  176. {
  177. //手动修正
  178. leftDaqiu[0][0] = 1175;  leftDaqiu[0][1] = 7;     rightDaqiu[0][0] = 256;  rightDaqiu[0][1] = 1;
  179. leftDaqiu[1][0] = 823;   leftDaqiu[1][1] = 603;   rightDaqiu[1][0] = 289;  rightDaqiu[1][1] = 431;
  180. leftDaqiu[2][0] = 963;   leftDaqiu[2][1] = 360;   rightDaqiu[2][0] = 283;  rightDaqiu[2][1] = 169;
  181. leftDaqiu[3][0] = 1065;  leftDaqiu[3][1] = 180;   rightDaqiu[3][0] = 294;  rightDaqiu[3][1] = 1;
  182. leftDaqiu[4][0] = 1039;  leftDaqiu[4][1] = 217;   rightDaqiu[4][0] = 314;  rightDaqiu[4][1] = 68;
  183. leftDaqiu[5][0] = 896;   leftDaqiu[5][1] = 448;   rightDaqiu[5][0] = 378;  rightDaqiu[5][1] = 402;
  184. leftDaqiu[6][0] = 933;   leftDaqiu[6][1] = 376;   rightDaqiu[6][0] = 398;  rightDaqiu[6][1] = 347;
  185. leftDaqiu[7][0] = 868;   leftDaqiu[7][1] = 463;   rightDaqiu[7][0] = 423;  rightDaqiu[7][1] = 418;
  186. leftDaqiu[8][0] = 878;   leftDaqiu[8][1] = 417;   rightDaqiu[8][0] = 458;  rightDaqiu[8][1] = 466;
  187. leftDaqiu[9][0] = 860;   leftDaqiu[9][1] = 423;   rightDaqiu[9][0] = 481;  rightDaqiu[9][1] = 490;
  188. leftDaqiu[10][0] = 840;  leftDaqiu[10][1] = 442;  rightDaqiu[10][0] = 499; rightDaqiu[10][1] = 500;
  189. leftDaqiu[11][0] = 822;  leftDaqiu[11][1] = 414;  rightDaqiu[11][0] = 523; rightDaqiu[11][1] = 511;
  190. leftDaqiu[12][0] = 805;  leftDaqiu[12][1] = 406;  rightDaqiu[12][0] = 538; rightDaqiu[12][1] = 516;
  191. leftDaqiu[13][0] = 802;  leftDaqiu[13][1] = 402;  rightDaqiu[13][0] = 549; rightDaqiu[13][1] = 514;
  192. leftXiaoqiu[0][0] = 1250;   leftXiaoqiu[0][1] = 120;   rightXiaoqiu[0][0] = 308;  rightXiaoqiu[0][1] = 313;
  193. leftXiaoqiu[1][0] = 1034;   leftXiaoqiu[1][1] = 481;   rightXiaoqiu[1][0] = 314;  rightXiaoqiu[1][1] = 482;
  194. leftXiaoqiu[2][0] = 1207;   leftXiaoqiu[2][1] = 228;   rightXiaoqiu[2][0] = 284;  rightXiaoqiu[2][1] = 186;
  195. leftXiaoqiu[3][0] = 1343;   leftXiaoqiu[3][1] = 55;    rightXiaoqiu[3][0] = 252;  rightXiaoqiu[3][1] = -20;
  196. leftXiaoqiu[4][0] = 1326;   leftXiaoqiu[4][1] = 102;   rightXiaoqiu[4][0] = 242;  rightXiaoqiu[4][1] = 23;
  197. leftXiaoqiu[5][0] = 1021;   leftXiaoqiu[5][1] = 625;   rightXiaoqiu[5][0] = 269;  rightXiaoqiu[5][1] = 632;
  198. leftXiaoqiu[6][0] = 1123;   leftXiaoqiu[6][1] = 489;   rightXiaoqiu[6][0] = 241;  rightXiaoqiu[6][1] = 458;
  199. leftXiaoqiu[7][0] = 1147;   leftXiaoqiu[7][1] = 475;   rightXiaoqiu[7][0] = 224;  rightXiaoqiu[7][1] = 404;
  200. leftXiaoqiu[8][0] = 1078;   leftXiaoqiu[8][1] = 595;   rightXiaoqiu[8][0] = 223;  rightXiaoqiu[8][1] = 558;
  201. leftXiaoqiu[9][0] = 1062;   leftXiaoqiu[9][1] = 635;   rightXiaoqiu[9][0] = 216;  rightXiaoqiu[9][1] = 598;
  202. leftXiaoqiu[10][0] = 1080;  leftXiaoqiu[10][1] = 619;  rightXiaoqiu[10][0] = 201; rightXiaoqiu[10][1] = 576;
  203. leftXiaoqiu[11][0] = 1054;  leftXiaoqiu[11][1] = 690;  rightXiaoqiu[11][0] = 189; rightXiaoqiu[11][1] = 633;
  204. leftXiaoqiu[12][0] = 1046;  leftXiaoqiu[12][1] = 724;  rightXiaoqiu[12][0] = 179; rightXiaoqiu[12][1] = 655;
  205. leftXiaoqiu[13][0] = 1046;  leftXiaoqiu[13][1] = 726;  rightXiaoqiu[13][0] = 172; rightXiaoqiu[13][1] = 656;
  206. leftHuaqiu[0][0] = 1075;  leftHuaqiu[0][1] = 111;   rightHuaqiu[0][0] = 120;  rightHuaqiu[0][1] = -30;//出视场
  207. leftHuaqiu[1][0] = 708;   leftHuaqiu[1][1] = 810;   rightHuaqiu[1][0] = 142;  rightHuaqiu[1][1] = 382;
  208. leftHuaqiu[2][0] = 876;   leftHuaqiu[2][1] = 518;   rightHuaqiu[2][0] = 131;  rightHuaqiu[2][1] = 83;
  209. leftHuaqiu[3][0] = 1021;  leftHuaqiu[3][1] = 253;   rightHuaqiu[3][0] = 180;   rightHuaqiu[3][1] = -60;//出视场
  210. leftHuaqiu[4][0] = 1019;  leftHuaqiu[4][1] = 248;   rightHuaqiu[4][0] = 183;   rightHuaqiu[4][1] = -30;//出视场
  211. leftHuaqiu[5][0] = 764;   leftHuaqiu[5][1] = 654;   rightHuaqiu[5][0] = 278;  rightHuaqiu[5][1] = 437;
  212. leftHuaqiu[6][0] = 844;   leftHuaqiu[6][1] = 486;   rightHuaqiu[6][0] = 286;  rightHuaqiu[6][1] = 261;
  213. leftHuaqiu[7][0] = 852;   leftHuaqiu[7][1] = 425;   rightHuaqiu[7][0] = 305;  rightHuaqiu[7][1] = 206;
  214. leftHuaqiu[8][0] = 780;   leftHuaqiu[8][1] = 523;   rightHuaqiu[8][0] = 347;  rightHuaqiu[8][1] = 359;
  215. leftHuaqiu[9][0] = 757;   leftHuaqiu[9][1] = 530;   rightHuaqiu[9][0] = 363;  rightHuaqiu[9][1] = 382;
  216. leftHuaqiu[10][0] = 765;  leftHuaqiu[10][1] = 505;  rightHuaqiu[10][0] = 368; rightHuaqiu[10][1] = 345;
  217. leftHuaqiu[11][0] = 702;  leftHuaqiu[11][1] = 554;  rightHuaqiu[11][0] = 382; rightHuaqiu[11][1] = 398;
  218. leftHuaqiu[12][0] = 666;  leftHuaqiu[12][1] = 573;  rightHuaqiu[12][0] = 386; rightHuaqiu[12][1] = 408;
  219. leftHuaqiu[13][0] = 656;  leftHuaqiu[13][1] = 581;  rightHuaqiu[13][0] = 384; rightHuaqiu[13][1] = 398;
  220. }
  221. //************************************
  222. // 2016/12/2  by 小白
  223. // Method:    uv2xyz
  224. // FullName:  uv2xyz
  225. // Access:    public
  226. // Returns:   cv::Point3f       世界坐标
  227. // Qualifier: 根据左右相机中成像坐标求解空间坐标
  228. // Parameter: Point2f uvLeft        左相机中成像坐标
  229. // Parameter: Point2f uvRight       右相机中成像坐标
  230. //************************************
  231. Point3f uv2xyz(Point2f uvLeft,Point2f uvRight)
  232. {
  233. //  [u1]      |X|                     [u2]      |X|
  234. //Z*|v1| = Ml*|Y|                   Z*|v2| = Mr*|Y|
  235. //  [ 1]      |Z|                     [ 1]      |Z|
  236. //            |1|                               |1|
  237. Mat mLeftRotation = Mat(3,3,CV_32F,leftRotation);
  238. Mat mLeftTranslation = Mat(3,1,CV_32F,leftTranslation);
  239. Mat mLeftRT = Mat(3,4,CV_32F);//左相机M矩阵
  240. hconcat(mLeftRotation,mLeftTranslation,mLeftRT);
  241. Mat mLeftIntrinsic = Mat(3,3,CV_32F,leftIntrinsic);
  242. Mat mLeftM = mLeftIntrinsic * mLeftRT;
  243. //cout<<"左相机M矩阵 = "<<endl<<mLeftM<<endl;
  244. Mat mRightRotation = Mat(3,3,CV_32F,rightRotation);
  245. Mat mRightTranslation = Mat(3,1,CV_32F,rightTranslation);
  246. Mat mRightRT = Mat(3,4,CV_32F);//右相机M矩阵
  247. hconcat(mRightRotation,mRightTranslation,mRightRT);
  248. Mat mRightIntrinsic = Mat(3,3,CV_32F,rightIntrinsic);
  249. Mat mRightM = mRightIntrinsic * mRightRT;
  250. //cout<<"右相机M矩阵 = "<<endl<<mRightM<<endl;
  251. //最小二乘法A矩阵
  252. Mat A = Mat(4,3,CV_32F);
  253. A.at<float>(0,0) = uvLeft.x * mLeftM.at<float>(2,0) - mLeftM.at<float>(0,0);
  254. A.at<float>(0,1) = uvLeft.x * mLeftM.at<float>(2,1) - mLeftM.at<float>(0,1);
  255. A.at<float>(0,2) = uvLeft.x * mLeftM.at<float>(2,2) - mLeftM.at<float>(0,2);
  256. A.at<float>(1,0) = uvLeft.y * mLeftM.at<float>(2,0) - mLeftM.at<float>(1,0);
  257. A.at<float>(1,1) = uvLeft.y * mLeftM.at<float>(2,1) - mLeftM.at<float>(1,1);
  258. A.at<float>(1,2) = uvLeft.y * mLeftM.at<float>(2,2) - mLeftM.at<float>(1,2);
  259. A.at<float>(2,0) = uvRight.x * mRightM.at<float>(2,0) - mRightM.at<float>(0,0);
  260. A.at<float>(2,1) = uvRight.x * mRightM.at<float>(2,1) - mRightM.at<float>(0,1);
  261. A.at<float>(2,2) = uvRight.x * mRightM.at<float>(2,2) - mRightM.at<float>(0,2);
  262. A.at<float>(3,0) = uvRight.y * mRightM.at<float>(2,0) - mRightM.at<float>(1,0);
  263. A.at<float>(3,1) = uvRight.y * mRightM.at<float>(2,1) - mRightM.at<float>(1,1);
  264. A.at<float>(3,2) = uvRight.y * mRightM.at<float>(2,2) - mRightM.at<float>(1,2);
  265. //最小二乘法B矩阵
  266. Mat B = Mat(4,1,CV_32F);
  267. B.at<float>(0,0) = mLeftM.at<float>(0,3) - uvLeft.x * mLeftM.at<float>(2,3);
  268. B.at<float>(1,0) = mLeftM.at<float>(1,3) - uvLeft.y * mLeftM.at<float>(2,3);
  269. B.at<float>(2,0) = mRightM.at<float>(0,3) - uvRight.x * mRightM.at<float>(2,3);
  270. B.at<float>(3,0) = mRightM.at<float>(1,3) - uvRight.y * mRightM.at<float>(2,3);
  271. Mat XYZ = Mat(3,1,CV_32F);
  272. //采用SVD最小二乘法求解XYZ
  273. solve(A,B,XYZ,DECOMP_SVD);
  274. //cout<<"空间坐标为 = "<<endl<<XYZ<<endl;
  275. //世界坐标系中坐标
  276. Point3f world;
  277. world.x = XYZ.at<float>(0,0);
  278. world.y = XYZ.at<float>(1,0);
  279. world.z = XYZ.at<float>(2,0);
  280. return world;
  281. }
  282. //************************************
  283. // Description: 将空间坐标转换为像面坐标,用于检验
  284. // Method:    xyz2uv
  285. // FullName:  xyz2uv
  286. // Access:    public
  287. // Parameter: Point3f worldPoint
  288. // Parameter: float intrinsic[3][3]
  289. // Parameter: float translation[1][3]
  290. // Parameter: float rotation[3][3]
  291. // Returns:   cv::Point2f
  292. // Author:    bhy
  293. // Date:      2016/12/28
  294. // History:
  295. //************************************
  296. Point2f xyz2uv(Point3f worldPoint,float intrinsic[3][3],float translation[1][3],float rotation[3][3])
  297. {
  298. //    [fx s x0]                         [Xc]        [Xw]        [u]   1     [Xc]
  299. //K = |0 fy y0|       TEMP = [R T]      |Yc| = TEMP*|Yw|        | | = —*K *|Yc|
  300. //    [ 0 0 1 ]                         [Zc]        |Zw|        [v]   Zc    [Zc]
  301. //                                                  [1 ]
  302. Point3f c;
  303. c.x = rotation[0][0]*worldPoint.x + rotation[0][1]*worldPoint.y + rotation[0][2]*worldPoint.z + translation[0][0]*1;
  304. c.y = rotation[1][0]*worldPoint.x + rotation[1][1]*worldPoint.y + rotation[1][2]*worldPoint.z + translation[0][1]*1;
  305. c.z = rotation[2][0]*worldPoint.x + rotation[2][1]*worldPoint.y + rotation[2][2]*worldPoint.z + translation[0][2]*1;
  306. Point2f uv;
  307. uv.x = (intrinsic[0][0]*c.x + intrinsic[0][1]*c.y + intrinsic[0][2]*c.z)/c.z + 0.5;//加0.5去整 == 四舍五入
  308. uv.y = (intrinsic[1][0]*c.x + intrinsic[1][1]*c.y + intrinsic[1][2]*c.z)/c.z + 0.5;//加0.5去整 == 四舍五入
  309. return uv;
  310. }
  311. //************************************
  312. // Description: 对畸变校正文件夹下的图片批量进行亮度/对比度调节
  313. // Method:    ContrastAndBright
  314. // FullName:  ContrastAndBright
  315. // Access:    public
  316. // Parameter: double alpha
  317. // Parameter: double beta
  318. // Returns:   void
  319. // Author:    bhy
  320. // Date:      2016/12/28
  321. // History:
  322. //************************************
  323. void ContrastAndBright(double alpha, double beta)
  324. {
  325. //double alpha =3;
  326. //double beta = 40;
  327. cout<<"**********************************************"<<endl;
  328. cout<<"                 亮度/对比度调节              "<<endl;
  329. cout<<"**********************************************"<<endl;
  330. cout<<"当前 alpha = "<<alpha<<endl;
  331. cout<<"当前 beta = "<<beta<<endl;
  332. Mat src,dst;
  333. system("md 亮度对比度\\rightky1");
  334. //右相机调节
  335. //如果校正图像目录不存在,则创建该目录
  336. for (int ii=1; ii<=PicNum; ii++)
  337. {
  338. cout<<"右:第"<<ii<<"张图片"<<endl;
  339. char* filename = new char[100];
  340. sprintf(filename,"畸变校正/rightky1/r%d.bmp",ii);
  341. src = imread(filename);//顺次读入图片
  342. delete []filename;//释放字符串
  343. dst = Mat::zeros(src.size(),src.type());//清空目标矩阵
  344. //根据alpha,beta重新计算灰度值
  345. for (int i = 0;i<src.rows;++i)
  346. for(int j= 0;j<src.cols;++j)
  347. for(int k = 0;k<3;++k)
  348. dst.at<Vec3b>(i,j)[k] = saturate_cast<uchar>(src.at<Vec3b>(i,j)[k]*alpha+beta);
  349. char* output = new char[100];
  350. sprintf(output,"亮度对比度/rightky1/r%d.bmp",ii);
  351. imwrite(output,dst); //顺次保存校正图
  352. delete []output;//释放字符串
  353. }
  354. //左相机调节
  355. //如果校正图像目录不存在,则创建该目录
  356. system("md 亮度对比度\\leftky1");
  357. for (int ii=1; ii<=PicNum; ii++)
  358. {
  359. cout<<"左:第"<<ii<<"张图片"<<endl;
  360. char* filename = new char[100];
  361. sprintf(filename,"畸变校正/leftky1/l%d.bmp",ii);
  362. src = imread(filename);//顺次读入图片
  363. delete []filename;//释放字符串
  364. dst = Mat::zeros(src.size(),src.type());//清空目标矩阵
  365. //根据alpha,beta重新计算灰度值
  366. for (int i = 0;i<src.rows;++i)
  367. for(int j= 0;j<src.cols;++j)
  368. for(int k = 0;k<3;++k)
  369. dst.at<Vec3b>(i,j)[k] = saturate_cast<uchar>(src.at<Vec3b>(i,j)[k]*alpha+beta);
  370. char* output = new char[100];
  371. sprintf(output,"亮度对比度/leftky1/l%d.bmp",ii);
  372. imwrite(output,dst); //顺次保存校正图
  373. delete []output;//释放字符串
  374. }
  375. cout<<"**********************************************"<<endl;
  376. cout<<"             亮度/对比度调节结束              "<<endl;
  377. cout<<"**********************************************"<<endl;
  378. }
  379. //************************************
  380. // Description: 对素材文件夹中的图片批量进行畸变校正
  381. // Method:    CorrectionProcess
  382. // FullName:  CorrectionProcess
  383. // Access:    public
  384. // Returns:   void
  385. // Author:    bhy
  386. // Date:      2016/12/28
  387. // History:
  388. //************************************
  389. void CorrectionProcess()
  390. {
  391. cout<<"**********************************************"<<endl;
  392. cout<<"                    畸变校正                  "<<endl;
  393. cout<<"**********************************************"<<endl;
  394. Mat image;
  395. //使用畸变系数与内参校正右相机原图
  396. //如果校正图像目录不存在,则创建该目录
  397. system("md 畸变校正\\rightky1");
  398. for (int i=1; i<=PicNum; i++)
  399. {
  400. cout<<"右:校正第"<<i<<"张图片"<<endl;
  401. char* filename = new char[100];
  402. sprintf(filename,"素材/rightky1/r%d.bmp",i);
  403. image = imread(filename,IMREAD_GRAYSCALE);//顺次读入图片
  404. delete []filename;//释放字符串
  405. char* output = new char[100];
  406. sprintf(output,"畸变校正/rightky1/r%d.bmp",i);
  407. imwrite(output,PictureCorrection(image,rightIntrinsic,rightDistortion)); //顺次保存校正图
  408. delete []output;//释放字符串
  409. }
  410. //使用畸变系数与内参校正左相机原图
  411. //如果校正图像目录不存在,则创建该目录
  412. system("md 畸变校正\\leftky1");
  413. for (int i=1; i<=PicNum; i++)
  414. {
  415. cout<<"左:校正第"<<i<<"张图片"<<endl;
  416. char* filename = new char[100];
  417. sprintf(filename,"素材/leftky1/l%d.bmp",i);
  418. image = imread(filename,IMREAD_GRAYSCALE);//顺次读入图片
  419. delete []filename;//释放字符串
  420. char* output = new char[100];
  421. sprintf(output,"畸变校正/leftky1/l%d.bmp",i);
  422. imwrite(output,PictureCorrection(image,leftIntrinsic,leftDistortion)); //顺次保存校正图
  423. delete []output;//释放字符串
  424. }
  425. cout<<"**********************************************"<<endl;
  426. cout<<"                  畸变校正结束                "<<endl;
  427. cout<<"**********************************************"<<endl;
  428. }
  429. //************************************
  430. // Method:    PictureCorrection
  431. // FullName:  PictureCorrection
  432. // Access:    public
  433. // Returns:   cv::Mat   校正图
  434. // Qualifier: 根据畸变系数与内参校正一张图片
  435. // Parameter: Mat image
  436. // Parameter: float intrinsic[3][3]     内参
  437. // Parameter: float distortion[1][5]    畸变矩阵
  438. //************************************
  439. Mat PictureCorrection( Mat image ,float intrinsic[3][3],float distortion[1][5])
  440. {
  441. Size image_size = image.size();
  442. Mat intrinsic_matrix = Mat(3,3,CV_32FC1,intrinsic);
  443. Mat distortion_coeffs = Mat(1,5,CV_32FC1,distortion);
  444. Mat R = Mat::eye(3,3,CV_32F);
  445. Mat mapx = Mat(image_size,CV_32FC1);
  446. Mat mapy = Mat(image_size,CV_32FC1);
  447. initUndistortRectifyMap(intrinsic_matrix,distortion_coeffs,R,intrinsic_matrix,image_size,CV_32FC1,mapx,mapy);
  448. Mat t = image.clone();
  449. cv::remap( image, t, mapx, mapy, INTER_LINEAR);
  450. return t;
  451. }
  452. //************************************
  453. // Description: 采用hough变换求取大球圆心
  454. // Method:    Daqiu
  455. // FullName:  Daqiu
  456. // Access:    public
  457. // Returns:   void
  458. // Author:    bhy
  459. // Date:      2016/12/28
  460. // History:
  461. //************************************
  462. void Daqiu()
  463. {
  464. cout<<"**********************************************"<<endl;
  465. cout<<"                  计算大球圆心                "<<endl;
  466. cout<<"**********************************************"<<endl;
  467. Mat img;
  468. //右相机 ContrastAndBright(2.5,50);
  469. system("md 大球圆心\\rightky1");
  470. cout<<"右图:"<<endl;
  471. for (int i=1; i<=PicNum; i++)
  472. {
  473. stringstream strStm;
  474. string strFileName;
  475. strStm << i;
  476. strStm >> strFileName;
  477. strFileName = "亮度对比度/rightky1/r" + strFileName + ".bmp";
  478. img = imread(strFileName,IMREAD_GRAYSCALE);
  479. GaussianBlur(img,img,Size(5,5),0);
  480. vector<Vec3f> circles;
  481. HoughCircles( img, circles, CV_HOUGH_GRADIENT, 3 ,70, 70, 30, 95 ,100);//hough圆变换
  482. Point2f center(0,0);
  483. float radius;
  484. for (int j = 0; j < circles.size(); j++)
  485. {
  486. if (circles[j][0] > center.x && circles[j][0] < img.cols/2)
  487. {
  488. center.x = circles[j][0];
  489. center.y = circles[j][1];
  490. radius = circles[j][2];//半径
  491. }
  492. }
  493. rightDaqiu[i-1][0] = center.x;
  494. rightDaqiu[i-1][1] = center.y;
  495. CvScalar color = CV_RGB(0,0,0);
  496. circle( img, (Point)center, radius, color, 3, 8, 0);//绘制圆
  497. circle( img, (Point)center, 3, color, 3, 8, 0);//绘制圆心
  498. cout<<"圆心:"<<center<<endl;//为了保证精度,以原值输出
  499. char* output = new char[100];
  500. sprintf(output,"大球圆心/rightky1/r%d.bmp",i);
  501. imwrite(output,img); //顺次保存校正图
  502. delete []output;//释放字符串
  503. }
  504. system("md 大球圆心\\leftky1");
  505. cout<<"左图:"<<endl;
  506. for (int i=1; i<=PicNum; i++)
  507. {
  508. stringstream strStm;
  509. string strFileName;
  510. strStm << i;
  511. strStm >> strFileName;
  512. strFileName = "亮度对比度/leftky1/l" + strFileName + ".bmp";
  513. img = imread(strFileName,IMREAD_GRAYSCALE);
  514. GaussianBlur(img,img,Size(5,5),0);
  515. vector<Vec3f> circles;
  516. HoughCircles( img, circles, CV_HOUGH_GRADIENT, 3 ,70, 30, 50, 100 ,110);//hough圆变换
  517. Point2f center(0,1024);
  518. float radius;
  519. for (int j = 0; j < circles.size(); j++)
  520. {
  521. if (circles[j][1]<center.y)
  522. {
  523. center.x = circles[j][0];
  524. center.y = circles[j][1];
  525. radius = circles[j][2];//半径
  526. }
  527. }
  528. leftDaqiu[i-1][0] = center.x;
  529. leftDaqiu[i-1][1] = center.y;
  530. CvScalar color = CV_RGB(0,0,0);
  531. circle( img, center, radius, color, 3, 8, 0);//绘制圆
  532. circle( img, center, 3, color, 3, 8, 0);//绘制圆心
  533. cout<<"圆心:"<<center<<endl;//为了保证精度,以原值输出
  534. char* output = new char[100];
  535. sprintf(output,"大球圆心/leftky1/l%d.bmp",i);
  536. imwrite(output,img); //顺次保存校正图
  537. delete []output;//释放字符串
  538. }
  539. cout<<"**********************************************"<<endl;
  540. cout<<"                  圆心计算结束                "<<endl;
  541. cout<<"**********************************************"<<endl;
  542. }

运行该程序,会提取生成的“三维坐标.csv”中的空间坐标数据,并绘制运动轨迹需要注意的是,在“三维坐标.csv”文件中直接修改圆心坐标没有用,需要在工程中的initPos()修改。

[cpp] view plain copy

  1. clc;clear;
  2. M = csvread('三维坐标.csv',3,6,[3,6,16,8]);
  3. x = M(:,1);
  4. y = M(:,2);
  5. z = M(:,3);
  6. plot3(x,y,z,'r');
  7. %legend('大球');
  8. hold on
  9. M = csvread('三维坐标.csv',20,6,[20,6,33,8]);
  10. x = M(:,1);
  11. y = M(:,2);
  12. z = M(:,3);
  13. plot3(x,y,z,'g');
  14. %legend('小球');
  15. hold on
  16. M = csvread('三维坐标.csv',37,6,[37,6,50,8]);
  17. x = M(:,1);
  18. y = M(:,2);
  19. z = M(:,3);
  20. plot3(x,y,z,'b');
  21. %legend('花球');
  22. hold on
  23. legend('大球','小球','花球');
  24. title('小球运动轨迹');
  25. xlabel('x');
  26. ylabel('y');
  27. zlabel('z');
  28. grid on
  29. axis square

最后绘制的轨迹图

补一张世界坐标系的图

立体视觉标定源代码C++,简单粗暴!粗暴·······相关推荐

  1. 最简单直接粗暴的Mothur分析OTU教程

    最简单直接粗暴的Mothur分析OTU教程 废话不多说,首先你需要下载Mothur,直接百度去官网下载 第一步:准备需要分析OTU的序列文件(fasta格式) 一般通过载体连接.大肠杆菌克隆.测序得到 ...

  2. 如何快速做一个微信自动拉群机器人 足够简单 足够粗暴

    wechaty 首先,wechaty了解一下,文档链接:https://docs.chatie.io/v/zh/ 只需要6行代码,你就可以通过个人号搭建一个微信机器人功能 ,用来自动管理微信消息,是不 ...

  3. Android直播app源代码超简单气泡效果

    Android直播app源代码超简单气泡效果实现的相关代码 1.1 定义气泡 气泡效果我们关心的属性并不多,主要有这几种:半径.坐标.上升速度.水平平移速度.由于我们只在 View 内部使用,因此直接 ...

  4. 双目立体视觉案例源代码 基于HALCCN的双目立体视觉系统实现 基于openev的双目测距 双目测距-opency 通用化视堂系线板架 Halcon视觉例子程序

    双目立体视觉案例源代码 双目测距 双目摄像头图像获取 标定代码 1.Halcoa 10三椎视党 2.Halcon视觉例子程序 wa2013 3.OpenCV+OpencL.双目立体视觉三幢重建 vs2 ...

  5. OpenCV实现张正友相机标定源代码

    本源代码基于VC++和opencv Opencv2.4.13.6版本开发,实现张正友相机标定源代码,资源包括完整源代码和12张棋盘图片,完美运行. Opencv2.4.13.6安装包下载地址:http ...

  6. Github邮件联系项目源代码作者简单方法

    Github邮件联系项目源代码作者简单方法 有时我们在导入别人github项目时有一些源代码的问题无法自行解决时就需要联系该github项目源代码的作者. 一.方法就是首先在github上找到gith ...

  7. MP3切分工具, 简单,粗暴好用。

    良友刚起步,请推广加关注 简单粗暴,傻瓜式mp3切割软件 简单粗暴,傻瓜式mp3切割软件 简单粗暴,傻瓜式mp3切割软件 下载地址 MP3切割软件(超好用).zip: https://545c.com ...

  8. linux学习笔记,简单的粗暴使用教学

    :我们这个课程基于CentOS 7 版本的学习 学习方式: 1.认识linux 2.基本的命令(重点:git讲了一些基本的命令(文件操作.目录管理.文件属性.vim编辑器.账号管理.磁盘管理-)) 3 ...

  9. 用Python选取神股(简单、粗暴)

    股市中不缺乏神话,追到神话不容易,在股市每日都能追到这些股票,那一定是神. 但是看看神话却不难,让我们先羡慕一下"神"的生活. 这个方法并不复杂,但是我们可以每日把最近N日涨幅或跌 ...

最新文章

  1. Bridge网络模式下Linux虚拟机和主机进行通信
  2. 【Android】5.3 单选和复选
  3. 计算机的桌面教案,《认识计算机桌面》教案-20210608141312.pdf-原创力文档
  4. java中对JVM的深度解析、调优工具、垃圾回收
  5. aaynctask控制多个下载进度_苍穹数码的“三调”全方位质量控制产品体系怎么样?...
  6. iOS开发里的Bundle是个啥玩意?!
  7. Spring 和 Spring Boot 最核心的 3 大区别,详解!
  8. [转]一篇很喜欢的知乎美文
  9. Android 动画(二)
  10. ImportError: cannot import name ‘constants‘
  11. vs2010 EF4.0 访问mysql
  12. mysql 源码设计,java+mysql大学网络社区平台设计+源代码
  13. 【五级流水线CPU】—— 7. 协处理器访问指令(2条)
  14. Function eregi() is deprecated
  15. 华为机试HJ44:Sudoku(数独问题,深度优先遍历DFS解法)
  16. python编写的购物网站_写一个购物商城的实例教程
  17. 提高办公效率的方法-工具篇
  18. 软件测试面试题【2021模拟面试整理版(含答案)】
  19. hsqldb mysql_hsqldb简单使用总结
  20. linux u盘 随身,教你安装CentOS到U盘,制作随身Linux系统

热门文章

  1. 传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确。参数 1 (“@xx“): 对于类型特定的元数据,数据类型 0x62 (sql_variant)的类型无效。
  2. xmlns是什么意思?
  3. 【趣味程序】输入一行字符,统计其中有多少个单词,单词之间用空格隔开
  4. Syncthing+蒲公英oray快速实现异地文件同步
  5. Openshift概念
  6. linux usb3.0移动硬盘,希捷(Seagate)1TB USB3.0移动硬盘使用评测
  7. java实现iam登录认证_是否可以通过Cloudfront对API网关进行IAM身份验证?
  8. 前端开发问题 大杂烩
  9. Arouter讲解4_Core,flutter游戏
  10. 实验室常用培养基配置 LB LA