【opencv学习-C++】 透视变换

男娘i 2021-12-23 23:55 711阅读 0赞

透视变换

先看一下图,在牌照的过程中,由于角度问题,难免会出现一些倾斜的问题,如下图,

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3BsU29uZ19DU0RO_size_16_color_FFFFFF_t_70

我们要解决的就是通过一系列的操作,将上图变为

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3BsU29uZ19DU0RO_size_16_color_FFFFFF_t_70 1

解决的思路

用opencv中的透视变换的API,输入四个角点的坐标,完成透视变换。

看下一主要的两个opecnv的API

  1. getPerspectiveTransform( InputArray src, InputArray dst );//获取透视变换矩阵
  2. warpPerspective( InputArray src, OutputArray dst,
  3. InputArray M, Size dsize,
  4. int flags = INTER_LINEAR,
  5. int borderMode = BORDER_CONSTANT,
  6. const Scalar& borderValue = Scalar());//透视变换

从函数的需求上可以看出,透视变换的主要任务就是:找到原图像的角点。主要流程如下

(1)灰度处理、二值化、形态学操作形成连通区域

(2)轮廓发现、将目标的轮廓绘制出来

(3)在绘制的轮廓中进行直线检测

(4)找出四条边,求出四个交点

(5)使用透视变换函数,得到结果

下面是主要的几个中间效果图

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3BsU29uZ19DU0RO_size_16_color_FFFFFF_t_70 2

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3BsU29uZ19DU0RO_size_16_color_FFFFFF_t_70 3

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3BsU29uZ19DU0RO_size_16_color_FFFFFF_t_70 4

完全示例代码

  1. #include<iostream>
  2. #include<opencv2\opencv.hpp>
  3. using namespace cv;
  4. using namespace std;
  5. int main()
  6. {
  7. //input image
  8. Mat src = imread("D:/images/shebaoka.png");
  9. imshow("input image", src);
  10. //bgr 2 gray 转为灰度图像
  11. Mat src_gray;
  12. cvtColor(src, src_gray, COLOR_BGR2GRAY);
  13. //binary 二值化
  14. Mat binary;
  15. threshold(src_gray, binary, 0, 255, THRESH_BINARY_INV|THRESH_OTSU); //THRESH_BINARY_INV二值化后取反
  16. //imshow("binary", binary);//因为有一些斑点存在
  17. //形态学 闭操作:可以填充小的区域
  18. Mat morhp_img;
  19. Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
  20. morphologyEx(binary, morhp_img, MORPH_CLOSE, kernel, Point(-1, -1), 3);
  21. //imshow("morphology", morhp_img);
  22. Mat dst;
  23. bitwise_not(morhp_img, dst);//在取反
  24. imshow("dst", dst);//
  25. //轮廓发现
  26. vector<vector<Point>> contous;
  27. vector<Vec4i> hireachy;
  28. findContours(dst, contous, hireachy, CV_RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
  29. cout << "contous.size:" << contous.size() << endl;
  30. //轮廓绘制
  31. int width = src.cols;
  32. int height = src.rows;
  33. Mat drawImage = Mat::zeros(src.size(), CV_8UC3);
  34. cout << contous.size() << endl;
  35. for (size_t t = 0; t < contous.size(); t++)
  36. {
  37. Rect rect = boundingRect(contous[t]);
  38. if (rect.width > width / 2 && rect.height > height / 2 && rect.width<width-5 && rect.height<height-5)
  39. {
  40. drawContours(drawImage, contous, static_cast<int>(t), Scalar(0, 0, 255), 2, 8, hireachy, 0, Point(0, 0));
  41. }
  42. }
  43. imshow("contours", drawImage);//显示找到的轮廓
  44. //直线检测
  45. vector<Vec4i> lines;
  46. Mat contoursImg;
  47. int accu = min(width*0.5, height*0.5);
  48. cvtColor(drawImage, contoursImg, COLOR_BGR2GRAY);
  49. imshow("contours", contoursImg);
  50. Mat linesImage = Mat::zeros(src.size(), CV_8UC3);
  51. HoughLinesP(contoursImg, lines, 1, CV_PI / 180.0, accu,accu,0);
  52. for (size_t t = 0; t < lines.size(); t++)
  53. {
  54. Vec4i ln = lines[t];
  55. line(linesImage, Point(ln[0], ln[1]), Point(ln[2], ln[3]), Scalar(0, 0, 255), 2, 8, 0);//绘制直线
  56. }
  57. cout << "number of lines:"<<lines.size() << endl;
  58. imshow("linesImages", linesImage);
  59. //寻找与定位上下 左右 四条直线
  60. int deltah = 0; //高度差
  61. int deltaw = 0; //宽度差
  62. Vec4i topLine, bottomLine; //直线定义
  63. Vec4i rightLine, leftLine;
  64. for (int i = 0; i < lines.size(); i++)
  65. {
  66. Vec4i ln = lines[i];//?????
  67. /*
  68. Opencv中的累计霍夫变换HoughLinesP(),输出的是一个Vector of Vec4i,
  69. Vector每个元素代表一条直线,是由一个4元浮点数构成,
  70. 前两个一组x_1,y_1,后两个一组x_2,y_2,代表了图像中直线的起始点和结束点。
  71. */
  72. deltah = abs(ln[3] - ln[1]); //计算高度差(y2-y1)
  73. //topLine
  74. if (ln[3] < height / 2.0 && ln[1] < height / 2.0 && deltah < accu - 1)
  75. {
  76. topLine = lines[i];
  77. }
  78. //bottomLine
  79. if (ln[3] > height / 2.0 && ln[1] >height / 2.0 && deltah < accu - 1)
  80. {
  81. bottomLine = lines[i];
  82. }
  83. deltaw = abs(ln[2] - ln[0]); //计算宽度差(x2-x1)
  84. //leftLine
  85. if (ln[0] < height / 2.0 && ln[2] < height / 2.0 && deltaw < accu - 1)
  86. {
  87. leftLine = lines[i];
  88. }
  89. //rightLine
  90. if (ln[0] > width / 2.0 && ln[2] >width / 2.0 && deltaw < accu - 1)
  91. {
  92. rightLine = lines[i];
  93. }
  94. }
  95. // 打印四条线的坐标
  96. cout << "topLine : p1(x,y)= " << topLine[0] << "," << topLine[1] << "; p2(x,y)= " << topLine[2] << "," << topLine[3] << endl;
  97. cout << "bottomLine : p1(x,y)= " << bottomLine[0] << "," << bottomLine[1] << "; p2(x,y)= " << bottomLine[2] << "," << bottomLine[3] << endl;
  98. cout << "leftLine : p1(x,y)= " << leftLine[0] << "," << leftLine[1] << "; p2(x,y)= " << leftLine[2] << "," << leftLine[3] << endl;
  99. cout << "rightLine : p1(x,y)= " << rightLine[0] << "," << rightLine[1] << "; p2(x,y)= " << rightLine[2] << "," << rightLine[3] << endl;
  100. //拟合四条直线
  101. float k1, k2, k3, k4, c1, c2, c3, c4;
  102. k1 = float(topLine[3] - topLine[1]) / float(topLine[2] - topLine[0]);
  103. c1 = topLine[1] - k1*topLine[0];
  104. k2 = float(bottomLine[3] - bottomLine[1]) / float(bottomLine[2] - bottomLine[0]);
  105. c2 = bottomLine[1] - k2*bottomLine[0];
  106. k3 = float(leftLine[3] - leftLine[1]) / float(leftLine[2] - leftLine[0]);
  107. c3 = leftLine[1] - k3*leftLine[0];
  108. k4 = float(rightLine[3] - rightLine[1]) / float(rightLine[2] - rightLine[0]);
  109. c4 = rightLine[1] - k4*rightLine[0];
  110. //求四个角点,
  111. Point p1;//topLine leftLine 左上角
  112. p1.x = static_cast<int>(c1 - c3) / k3 - k1;
  113. p1.y = k1*p1.x + c1;
  114. Point p2;//topLine rightLine 右上角
  115. p2.x = static_cast<int>(c1 - c4) / k4 - k1;
  116. p2.y = k1*p2.x + c1;
  117. Point p3;//bottomLine leftLine 左下角
  118. p3.x = static_cast<int>(c2 - c3) / k3 - k2;
  119. p3.y = k2*p3.x + c2;
  120. Point p4;//bottomLine rightLine 右下角
  121. p4.x = static_cast<int>(c2 - c4) / k4 - k2;
  122. p4.y = k2*p4.x + c2;
  123. cout << "Point p1: (" << p1.x << "," << p1.y << ")" << endl;
  124. cout << "Point p2: (" << p2.x << "," << p2.y << ")" << endl;
  125. cout << "Point p3: (" << p3.x << "," << p3.y << ")" << endl;
  126. cout << "Point p4: (" << p4.x << "," << p4.y << ")" << endl;
  127. //显示四个点
  128. circle(linesImage, p1, 2, Scalar(0,255, 0), 2);
  129. circle(linesImage, p2, 2, Scalar(0,255, 0), 2);
  130. circle(linesImage, p3, 2, Scalar(0, 255, 0), 2);
  131. circle(linesImage, p4, 2, Scalar(0, 255, 0), 2);
  132. imshow("find four points", linesImage);
  133. //透视变换
  134. vector<Point2f> src_corners(4);
  135. src_corners[0] = p1;
  136. src_corners[1] = p2;
  137. src_corners[2] = p3;
  138. src_corners[3] = p4;
  139. Mat result_images = Mat::zeros(height*0.7, width*0.9, CV_8UC3);
  140. vector<Point2f> dst_corners(4);
  141. dst_corners[0] = Point(0, 0);
  142. dst_corners[1] = Point(result_images.cols, 0);
  143. dst_corners[2] = Point(0, result_images.rows);
  144. dst_corners[3] = Point(result_images.cols, result_images.rows);
  145. Mat warpmatrix = getPerspectiveTransform(src_corners, dst_corners); //获取透视变换矩阵
  146. warpPerspective(src, result_images, warpmatrix, result_images.size()); //透视变换
  147. imshow("final result", result_images);
  148. imwrite("D:/images/warpPerspective.png", result_images);
  149. waitKey(0);
  150. return 0;
  151. }

控制台的输出

***** VIDEOINPUT LIBRARY - 0.1995 - TFW07 *****

contous.size:6
6
number of lines:5
topLine : p1(x,y)= 20,64; p2(x,y)= 487,56
bottomLine : p1(x,y)= 145,329; p2(x,y)= 361,332
leftLine : p1(x,y)= 13,75; p2(x,y)= 47,320
rightLine : p1(x,y)= 479,328; p2(x,y)= 497,66
Point p1: (11,64)
Point p2: (497,55)
Point p3: (47,327)
Point p4: (479,333)

发表评论

表情:
评论列表 (有 0 条评论,711人围观)

还没有评论,来说两句吧...

相关阅读

    相关 OpenCV 透视变换

    透视变换是将图像从一个视平面投影到另外一个视平面的过程,所以透视变换也被称为投影映射(Projection Mapping)。在图像的仿射变换中需要变换矩阵是一个2x3的两维平

    相关 OpenCV 透视变换

    透视变换是将图像从一个视平面投影到另外一个视平面的过程,所以透视变换也被称为投影映射(Projection Mapping)。在图像的仿射变换中需要变换矩阵是一个2x3的两维平