目的:将以下的图片转正显示
![](https://i-blog.csdnimg.cn/blog_migrate/9ec2553a28d40d910f53faa7a7772f16.png)
opencv自带的函数“透视变换”,但是有一点,四个交点的值我们是不知道的,有几种办法:
1.直接用鼠标去Image watch去查看四个交点的值。
2.用角点检测算法。。。我现在还没学到。
3.使用数学知识,求四条直线然后进行求取。
第一种很简单,分分钟实现了。第二种等以后学到再用。本文主要介绍第三种。
第一种方法实现:
1 Point p1; // 左上角
2 p1.x = 13;
3 p1.y = 65;
4 Point p2; // 右上角
5 p2.x = 474;
6 p2.y = 58;
7 Point p3; // 左下角
8 p3.x = 48;
9 p3.y = 310;
10 Point p4; // 右下角
11 p4.x = 461;
12 p4.y = 317;
13
14 // 透视变换
15 vector<Point2f> src_corners(4);
16 src_corners[0] = p1;
17 src_corners[1] = p2;
18 src_corners[2] = p3;
19 src_corners[3] = p4;
20 int width, height;
21 width = SourseImage.cols;
22 height = SourseImage.rows;
23
24 vector<Point2f> dst_corners(4);
25 dst_corners[0] = Point(0, 0);
26 dst_corners[1] = Point(width, 0);
27 dst_corners[2] = Point(0, height);
28 dst_corners[3] = Point(width, height);
29
30 // 获取透视变换矩阵
31 Mat warpmatrix = getPerspectiveTransform(src_corners, dst_corners);
32 warpPerspective(SourseImage, OutputImage, warpmatrix, SourseImage.size(), INTER_LINEAR);
33 imshow("123", SourseImage);
效果图如下:就是手工输入有点牵强~~
![](https://i-blog.csdnimg.cn/blog_migrate/1c8182c561b65057da21fe5d732307f2.png)
第三种方法实现:
高斯滤波-->>灰度-->>二值化
![](https://i-blog.csdnimg.cn/blog_migrate/2188961f9e11618e93a6924c320bd628.png)
形态学计算-->>反转
![](https://i-blog.csdnimg.cn/blog_migrate/8da4e4012695ef3663b274c0fde9a929.png)
边缘检测:
![](https://i-blog.csdnimg.cn/blog_migrate/6badffb702aa768318ed6bd085fb4da4.png)
直线检测:(这个参数得适当的调整,不然效果很差)
![](https://i-blog.csdnimg.cn/blog_migrate/91bf2e103deea68ddec6fe89628a0ad8.png)
数学方法求直线交点:
![](https://i-blog.csdnimg.cn/blog_migrate/a788c8f65ab3bf159666e44875e2af37.png)
透视变换:
![](https://i-blog.csdnimg.cn/blog_migrate/502351351d50b5f3aa96addcce97f620.png)
代码:
1 int main(int argc, char** argv)
2 {
3 SourseImage = imread("1.png");
4 if (SourseImage.empty()) return -1;
5 imshow("sourse", SourseImage);
6 //-------------------预处理----------------------//
7 GaussianBlur(SourseImage, MiddleImage, Size(3, 3), 3, 3);
8 cvtColor(MiddleImage, MiddleImage, CV_BGR2GRAY);
9 threshold(MiddleImage, MiddleImage, 0, 255, THRESH_BINARY | THRESH_OTSU);
10 imshow("threImage", MiddleImage);
11 //-------------------形态学操作-----------------//
12 Mat kernel = getStructuringElement(MORPH_RECT, Size(27, 27));
13 morphologyEx(MiddleImage, MiddleImage, MORPH_OPEN, kernel);
14 morphologyEx(MiddleImage, MiddleImage, MORPH_OPEN, kernel);
15 bitwise_not(MiddleImage, MiddleImage, Mat());
16 imshow("morphology", MiddleImage);
17 //----------------------边缘检测-----------------------------//
18 vector<vector<Point>> contours;
19 vector<Vec4i> hierarchy;
20 Mat testImage = Mat::zeros(MiddleImage.size(), MiddleImage.type());
21 findContours(MiddleImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
22 for (size_t i = 0; i < contours.size(); i++)
23 {
24 drawContours(testImage, contours, i, Scalar(255, 255, 255), 1);
25 }
26 imshow("contours", testImage);
27
28 int width = SourseImage.cols;
29 int height = SourseImage.rows;
30 //--------------------------霍夫直线检测------------------------//
31 vector<Vec4i> lines;
32 int accu = min(width*0.3, height*0.3);
33 HoughLinesP(testImage, lines, 1, CV_PI / 360, accu, accu, 40);//参数最好用图片的长宽变换来的,容易移植
34 Mat lineImage = Mat::zeros(MiddleImage.size(), MiddleImage.type());
35 for (size_t i = 0; i < lines.size(); i++)
36 {
37 line(lineImage, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(255, 255, 255), 1, 8, 0);
38 }
39 imshow("lines", lineImage);
40 //-----------------查找线段对应的矩形边界------------------//
41 Vec4i topline, bottomline, leftline, rightline;
42 for (size_t i = 0; i < lines.size(); i++)
43 {
44 double roti = abs(lines[i][3] - lines[i][1]);
45 //判断是平行X轴还是平行Y轴-->>|y2-y1|
46 if (roti < SourseImage.cols / 3)//平行X轴(上下两条边)
47 {
48 if (lines[i][1] < SourseImage.rows / 2 && lines[i][3] < SourseImage.rows / 2) topline = lines[i];
49 else bottomline = lines[i];
50 }
51 else//左右两条边
52 {
53 if (lines[i][0] < SourseImage.cols / 2 && lines[i][2] < SourseImage.cols / 2) leftline = lines[i];
54 else rightline = lines[i];
55 }
56 }
57 //--------------------计算四条线段的交点-----------------------//
58 double b_top, b_bot, b_rit, b_lef;
59 double k_top, k_bot, k_rit, k_lef;
60 Point lef_top, rit_top, lef_bot, rit_bot;
61 //直线斜率
62 k_top = (topline[3] - topline[1]) / (topline[2] - topline[0]);
63 k_bot = (bottomline[3] - bottomline[1]) / (bottomline[2] - bottomline[0]);
64 k_rit = (rightline[3] - rightline[1]) / (rightline[2] - rightline[0]);
65 k_lef = (leftline[3] - leftline[1]) / (leftline[2] - leftline[0]);
66 //直线表达式因子,推到一遍就知道了
67 b_top = topline[3] - k_top*topline[2];
68 b_bot = bottomline[3] - k_bot*bottomline[2];
69 b_rit = rightline[3] - k_rit*rightline[2];
70 b_lef = leftline[3] - k_lef*leftline[2];
71 //计算交点值
72 lef_top.x = abs((b_top - b_lef) / (k_top - k_lef));
73 rit_top.x = abs((b_top - b_rit) / (k_top - k_rit));
74 lef_bot.x = abs((b_bot - b_lef) / (k_bot - k_lef));
75 rit_bot.x = abs((b_bot - b_rit) / (k_bot - k_rit));
76
77 lef_top.y = abs(k_top*lef_top.x + b_top);
78 rit_top.y = abs(k_top*rit_top.x + b_top);
79 lef_bot.y = abs(k_bot*lef_bot.x + b_bot);
80 rit_bot.y = abs(k_bot*rit_bot.x + b_bot);
81 //画出交点
82 Mat lastImage = Mat::zeros(MiddleImage.size(), CV_8UC3);
83 circle(lastImage, lef_top, 5, Scalar(0, 0, 255));
84 circle(lastImage, rit_top, 5, Scalar(0, 0, 255));
85 circle(lastImage, lef_bot, 5, Scalar(0, 0, 255));
86 circle(lastImage, rit_bot, 5, Scalar(0, 0, 255));
87 imshow("circle", lastImage);
88 //-------------------------透视变换------------------------//
89 //存储需要变换的四点
90 vector<Point2f> srcPoint(4), dstPoint(4);
91 srcPoint[0] = lef_top;
92 srcPoint[1] = rit_top;
93 srcPoint[2] = lef_bot;
94 srcPoint[3] = rit_bot;
95
96 dstPoint[0] = Point2f(0, 0);
97 dstPoint[1] = Point2f(SourseImage.cols, 0);
98 dstPoint[2] = Point2f(0, SourseImage.rows);
99 dstPoint[3] = Point2f(SourseImage.cols, SourseImage.rows);
100
101 Mat Change = getPerspectiveTransform(srcPoint,dstPoint);
102 warpPerspective(SourseImage, OutputImage, Change, OutputImage.size());
103 imshow("test",OutputImage);
104 waitKey(0);
105 return 0;
106 }
其实那个阈值化也可以自己手动去调节:
g_Value =95;
void on_AdaptThreshold(int, void*){ int k = 2 * g_Value + 1; adaptiveThreshold(MiddleImage, OutputImage, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, k, 0); imshow("AdaptThreshold", OutputImage);}
参考:贾志刚opencv系列