经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
利用C++?OpenCV?实现从投影图像恢复仿射特性
来源:jb51  时间:2021/11/29 12:51:44  对本文有异议

原理

我们通过相机拍摄的图片存在各种畸变,其中投影畸变使得原本平行的直线不再平行,就会产生照片中近大远小的效果,要校正这一畸变,书中给了很多方法,这里是其中的一种。

我们可以将投影变换拆分成相似变换、仿射变换和投影变换三部分, 如下图,

其中相似变换和仿射变换不会改变infinite line,只有投影变换会改变。因此只要找到畸变图像中的这条线,就能够恢复图像的仿射特性(相当于逆转投影变换)。而要确定这条线的位置,就得至少知道线上的两个点。我们知道,所有平行线的交点都在infinite line上面,因此,我们只需要找到图像上的两对平行线(原本是平行,图像上不再平行),求出对应的两个交点,就能找到infinite line了,如下图

进而可以图像的恢复仿射特性。

实现思路

首先我们的畸变图像如下图,

利用公式:

  1. l = x1 × x2

可以通过x1、x2的齐次坐标求出两点连线l的齐次坐标。在图中我们找到两对平行线l1、l2和l3、l4,如下图

利用公式:

  1. x = l1 × l2

可以通过l1、l2以及l3、l4的齐次坐标分别求出两对平行线的交点A12、A34,直线A12A34就是我们要找的infinite line。假设该直线的齐次坐标为(l1,l2,l3),那么通过矩阵:

  1. H = ((100),(010),(l1l2l3))

就能够将直线(l1,l2,l3)变换成(0,0,1),即将该直线还原成为infinite line。同理我们也可以利用H矩阵,通过公式:

  1. x = Hx'

还原投影畸变。

主要代码

代码一共需要运行两次

第一次运行的主函数:

  1. int main()
  2. {
  3. Mat src = imread("distortion.jpg", IMREAD_GRAYSCALE);
  4. IplImage *src1 = cvLoadImage("distortion.jpg");
  5. //第一步,通过鼠标获取图片中某个点的坐标,运行第一步时注释掉Rectify(points_3d, src, src1);,将获取到的八个点写入
  6. //points_3d[8]坐标数组中,因为是齐次坐标,x3 = 1
  7. GetMouse(src1);
  8. //输入畸变图上的8个关键点
  9. Point3d points_3d[8] = { Point3d(99, 147, 1), Point3d(210, 93, 1), Point3d(144, 184, 1), Point3d(261, 122, 1),
  10. Point3d(144, 184, 1), Point3d(99, 147, 1), Point3d(261, 122, 1), Point3d(210, 93, 1) };
  11. //第二步,校正图像,运行此步骤时注释掉GetMouse(src1);,解除注释Rectify(points_3d, src, src1);
  12. //Rectify(points_3d, src, src1);
  13. imshow("yuantu", src);
  14. waitKey(0);
  15. }

其他函数:

  1. void on_mouse(int event, int x, int y, int flags, void* ustc)
  2. {
  3. CvFont font;
  4. cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1, CV_AA);
  5.  
  6. if (event == CV_EVENT_LBUTTONDOWN)
  7. {
  8. CvPoint pt = cvPoint(x, y);
  9. char temp[16];
  10. sprintf(temp, "(%d,%d)", pt.x, pt.y);
  11. cvPutText(src, temp, pt, &font, cvScalar(255, 255, 255, 0));
  12. cvCircle(src, pt, 2, cvScalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
  13. cvShowImage("src", src);
  14. }
  15. }
  16.  
  17. void GetMouse(IplImage *img)
  18. {
  19. src = img;
  20. cvNamedWindow("src", 1);
  21. cvSetMouseCallback("src", on_mouse, 0);
  22.  
  23. cvShowImage("src", src);
  24. waitKey(0);
  25. }

在弹出来的图片中点击任意地方可获得改点的图像坐标(x1,x2),如下图:

我选取了a、b、c、d四个点,其中:

  1. ab // cd ac // bd

将这四个点的坐标按照a、b、c、d、c、a、d、b的顺序填入points_3d[8]坐标数组中,第一次运行结束。

第二次运行的主函数:

  1. int main()
  2. {
  3. Mat src = imread("distortion.jpg", IMREAD_GRAYSCALE);
  4. IplImage *src1 = cvLoadImage("distortion.jpg");
  5. //第一步,通过鼠标获取图片中某个点的坐标,运行第一步时注释掉Rectify(points_3d, src, src1);,将获取到的八个点写入
  6. //points_3d[8]矩阵中,因为是齐次坐标,x3 = 1
  7. //GetMouse(src1);
  8. //输入畸变图上的8个关键点
  9. Point3d points_3d[8] = { Point3d(99, 147, 1), Point3d(210, 93, 1), Point3d(144, 184, 1), Point3d(261, 122, 1),
  10. Point3d(144, 184, 1), Point3d(99, 147, 1), Point3d(261, 122, 1), Point3d(210, 93, 1) };
  11. //第二步,校正图像,运行此步骤时注释掉GetMouse(src1);,解除注释Rectify(points_3d, src, src1);
  12. Rectify(points_3d, src, src1);
  13. imshow("yuantu", src);
  14. waitKey(0);
  15. }

校正函数:

  1. void Rectify(Point3d* points, Mat src, IplImage* img)
  2. {
  3. //通过输入的8个点得到4条连线
  4. vector<vector<float>> lines;
  5. int num_lines = 4;
  6. for(int i = 0; i < num_lines; i++)
  7. {
  8. //获取两点连线
  9. GetLineFromPoints(points[2 * i], points[2 * i + 1], lines);
  10. }
  11. //分别求取两个交点
  12. vector<Point3f> intersect_points;
  13. int num_intersect_points = 2;
  14. for (int i = 0; i < num_intersect_points; i++)
  15. {
  16. //计算交点
  17. GetIntersectPoint(lines[2 * i], lines[2 * i + 1], intersect_points);
  18. }
  19. //通过两个交点连线求消失线
  20. vector<vector<float>> vanishing_line;
  21. GetLineFromPoints(intersect_points[0], intersect_points[1], vanishing_line);
  22. //恢复矩阵
  23. float H[3][3] = {{1, 0, 0},
  24. {0, 1, 0},
  25. {vanishing_line[0][0], vanishing_line[0][1], vanishing_line[0][2]}};
  26. Mat image = Mat::zeros(src.rows, src.cols, CV_8UC1);
  27. GetRectifingImage(vanishing_line[0], src, image);
  28. int i = 0;
  29. }
  30.  
  31. void GetLineFromPoints(Point3d point1, Point3d point2, vector<vector<float>> &lines)
  32. {
  33. vector<float> line;
  34. //定义直线的三个齐次坐标
  35. float l1 = 0;
  36. float l2 = 0;
  37. float l3 = 0;
  38. l1 = (point1.y * point2.z - point1.z * point2.y);
  39. l2 = (point1.z * point2.x - point1.x * point2.z);
  40. l3 = (point1.x * point2.y - point1.y * point2.x);
  41. //归一化
  42. l1 = l1 / l3;
  43. l2 = l2 / l3;
  44. l3 = 1;
  45. line.push_back(l1);
  46. line.push_back(l2);
  47. line.push_back(l3);
  48. lines.push_back(line);
  49. }
  50.  
  51. void GetIntersectPoint(vector<float> line1, vector<float> line2, vector<Point3f> &intersect_points)
  52. {
  53. Point3f intersect_point;
  54. //定义交点的三个齐次坐标
  55. float x1 = 0;
  56. float x2 = 0;
  57. float x3 = 0;
  58. x1 = (line1[1] * line2[2] - line1[2] * line2[1]);
  59. x2 = (line1[2] * line2[0] - line1[0] * line2[2]);
  60. x3 = (line1[0] * line2[1] - line1[1] * line2[0]);
  61. //归一化
  62. x1 = x1 / x3;
  63. x2 = x2 / x3;
  64. x3 = 1;
  65. intersect_point.x = x1;
  66. intersect_point.y = x2;
  67. intersect_point.z = x3;
  68. intersect_points.push_back(intersect_point);
  69. }
  70.  
  71. int Round(float x)
  72. {
  73. return (x > 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
  74. }
  75.  
  76. void GetRectifingImage(vector<float> line, Mat src, Mat dst)
  77. {
  78. Size size_src = src.size();
  79. for (int i = 0; i < size_src.height; i++)
  80. {
  81. for (int j = 0; j < size_src.width; j++)
  82. {
  83. float x3 = line[0] * j + line[1] * i + line[2] * 1;
  84. int x1 = Round(j / x3);
  85. int x2 = Round(i / x3);
  86. if (x1 < size_src.width && x1 >= 0 && x2 < size_src.height && x2 >= 0)
  87. {
  88. dst.at<uint8_t>(x2, x1) = src.at<uint8_t>(i, j);
  89. }
  90. }
  91. }
  92. imshow("src", src);
  93. imshow("dst", dst);
  94. waitKey(0);
  95. }

运行结果如下图:

校正效果和点的选取有关,因为鼠标点击的那个点不一定是我们真正想要的点,建议一条直线的的两个点间距尽量大一些。

完整代码链接??提取码:qltt?

到此这篇关于利用C++ OpenCV 实现从投影图像恢复仿射特性的文章就介绍到这了,更多相关C++ OpenCV 投影图像恢复仿射特性内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号