目标:1.把矩形旋转正。
2.把文字旋转校正。
![](https://i-blog.csdnimg.cn/blog_migrate/afcecb98be3aa1fc7e27b424f1c2f912.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b0681e74bf8b36ed5699c9a06f2767bf.png)
目标一(旋转正方形)
思路:A.利用寻找边界进行旋转,然后进行ROI提取。
B.利用霍夫变换等寻找直线,主要找到拐点再进行图像变化。
本文利用第一点思路进行。。。
![](https://i-blog.csdnimg.cn/blog_migrate/b2777542818d135ee4a89505ef3a2b56.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7f103e26237b21aa6f2451658fdf785b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ec624115e1265941331815a60b5a084f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/836a4f01937eacc6d24d428135d42672.png)
![](https://i-blog.csdnimg.cn/blog_migrate/caba59e3a2e56b9da837d7564aff6205.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d0f72589e42c785c9a8dad361b40e78a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2ae2a7a15503e1455aa9708b785af101.png)
![](https://i-blog.csdnimg.cn/blog_migrate/dcb65b3e76df4bc47406e27456a93124.png)
程序很简单,直接上代码:
1 #include<iostream>
2 #include <opencv2/opencv.hpp>
3 #include <math.h>
4 using namespace cv;
5 using namespace std;
6
7 int Threshold_Value = 50;
8 const int Threshold_Max_value = 255;
9 const int Threshold_type_value = 3;
10 double MaxWidth = 0, MaxHeight = 0;//找最大的矩形边
11
12 RNG rng(12345);
13
14 Mat input_image, threshold_image, output_image, Middle_image;
15
16 void Threshold_Image_Bar(int, void *);
17
18 int main(int argc, char**argv)
19 {
20 input_image = imread("1.jpg");
21 if (input_image.data == NULL) {
22 return -1; cout << "can't open image.../";
23 }
24 imshow("Sourse Image", input_image);
25 blur(input_image, Middle_image,Size(3,3),Point(-1,-1),4);
26 imshow("Blur Image", Middle_image);
27 cvtColor(Middle_image, Middle_image,COLOR_RGB2GRAY);
28 imshow("Gray Image", Middle_image);
29 namedWindow("Threshold Image",1);
30 createTrackbar("阈值调整", "Threshold Image",&Threshold_Value,255,Threshold_Image_Bar);
31 Threshold_Image_Bar(0,0);
32 waitKey(0);
33 return 0;
34 }
35
36 void Threshold_Image_Bar(int, void *)
37 {
38 /*--------------------------------------------------------------------------------------------------------------------------*/
39 /*--------------------------------------------------------------------------------------------------------------------------*/
40 /*-----------------------------------------------------图像旋转校正---------------------------------------------------------*/
41 /*--------------------------------------------------------------------------------------------------------------------------*/
42 /*--------------------------------------------------------------------------------------------------------------------------*/
43
44 threshold(Middle_image, threshold_image, 90, 255, 3);
45 Canny(threshold_image,threshold_image, Threshold_Value, Threshold_Value*3);
46 imshow("Threshold Image", threshold_image);
47
48 vector<vector<Point>> contours;
49 vector<Vec4i> hireachy;
50 findContours(threshold_image,contours,hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE,Point(-1,-1));
51 char flag_count = 0;
52 Mat Show_threImage = Mat::zeros(threshold_image.size(),CV_8UC3);
53 RotatedRect MinRect;
54 for (size_t i = 0; i < contours.size(); i++)
55 {
56 const Scalar color = Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255));
57 drawContours(Show_threImage,contours,static_cast<int>(i),color,2,8,hireachy,0,Point());
58 MinRect = minAreaRect(contours[i]);
59 //---------------------找最大的长宽、边界-----------------------------//
60 MaxWidth = MaxWidth > MinRect.size.width ? MaxWidth : MinRect.size.width;
61 MaxHeight = MaxHeight > MinRect.size.height ? MaxHeight : MinRect.size.height;
62 flag_count = ((MaxWidth == MinRect.size.width) || (MaxHeight == MinRect.size.height)) ? static_cast<int>(i) : flag_count;
63 }
64 imshow("Draw_Image_Contours", Show_threImage);
65 //-----------------为了求矩形边角点坐标---------------------//
66 Point2f pt[4];
67 MinRect = minAreaRect(contours[flag_count]);
68 MinRect.points(pt);
69 Mat MaxRectImage = Mat::zeros(input_image.size(),CV_8UC3);
70 for (size_t i = 0; i < 4; i++)
71 {
72 const Scalar color = Scalar(255,255,255);
73 line(MaxRectImage,Point(pt[i]),Point(pt[(i+1)%4]),color);
74 }
75 imshow("MaxRectImage", MaxRectImage);
76 //------水漫操作,为了就倾斜矩形座位ROI,在这里没作用,就是为了看看而已----//
77 Mat gray;
78 gray.create(input_image.size(), input_image.type());
79 Rect s = boundingRect(contours[flag_count]);
80 floodFill(MaxRectImage, Point(s.x + s.width / 2, s.y + s.height / 2), Scalar(255, 255, 255));
81 bitwise_and(input_image, MaxRectImage, gray);
82 imshow("wjy", gray);
83 //--------图像旋转-----------//
84 Mat RotateImage = getRotationMatrix2D(Point2f(input_image.cols / 2, input_image.rows / 2), 90+MinRect.angle, 1.0);
85 warpAffine(input_image, input_image,RotateImage, input_image.size(),1,0,Scalar(255,255,255));
86 imshow("RotateImage", input_image);
87
88 /*--------------------------------------------------------------------------------------------------------------------------*/
89 /*--------------------------------------------------------------------------------------------------------------------------*/
90 /*------------------------------------------------ROI区域进行充满图像操作---------------------------------------------------*/
91 /*--------------------------------------------------------------------------------------------------------------------------*/
92 /*--------------------------------------------------------------------------------------------------------------------------*/
93
94 Mat test;
95 blur(input_image, test, Size(3, 3), Point(-1, -1), 4);
96 cvtColor(test, test, COLOR_RGB2GRAY);
97 MaxWidth = 0; MaxHeight = 0;
98 threshold(test, test, 90, 255, 3);
99
100 Canny(test, test, Threshold_Value, Threshold_Value * 3);
101 findContours(test, contours, hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));
102 for (size_t i = 0; i < contours.size(); i++)
103 {
104 const Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
105 drawContours(test, contours, static_cast<int>(i), color, 2, 8, hireachy, 0, Point());
106 MinRect = minAreaRect(contours[i]);
107
108 MaxWidth = MaxWidth > MinRect.size.width ? MaxWidth : MinRect.size.width;
109 MaxHeight = MaxHeight > MinRect.size.height ? MaxHeight : MinRect.size.height;
110 flag_count = ((MaxWidth == MinRect.size.width) || (MaxHeight == MinRect.size.height)) ? static_cast<int>(i) : flag_count;
111 }
112 MinRect = minAreaRect(contours[flag_count]);
113
114 /*通过水漫算法找ROI
115 Mat gray;
116 gray.create(input_image.size(), input_image.type());
117 Rect s = boundingRect(contours[flag_count]);
118 floodFill(MaxRectImage, Point(s.x + s.width / 2, s.y + s.height / 2), Scalar(255, 255, 255));
119 bitwise_and(input_image, MaxRectImage, gray);
120 imshow("wjy", gray);
121 */
122 /*通过矩形找ROI
123 Rect bbox = MinRect.boundingRect();
124 Mat wjy_image = input_image(bbox);
125 imshow("123", wjy_image);
126 */
127
128 /*通过四个点找ROI*/
129 MinRect.points(pt);
130 //Mat Result_Image = input_image(Rect(Point2i(100,100), Point2i(250, 250)));
131 Mat Result_Image = input_image(Rect(Point2i(pt[1]),Point2i(pt[3])));
132 Mat Result_ROI = Result_Image.clone();
133 imshow("123",Result_ROI);
134
135 }
目标二(旋转文字)
首先介绍一下DFT:
1.我们的图像是代表:空间域+值域。
(1)其中空间域就是像素直接的距离,之前的高斯滤波考虑的就是空间域,距离中心像素越近占的权重就越大(这个不懂去看我的高斯那篇博文)。
(2)其中值域就是像素值,之前的中值滤波考虑的就是像素值。
--->>>我现在想看看这个图像的边缘和纹理特征,也就是想看到图像哪里变化的快,图像的走向等,怎么办?如果是一条曲线,我们能看到线的走向,但是看得不明显,这个大家都知道,求f(x)的微分就可以了,看得不够细再求二阶微分。。。那么图像呢?边缘不就是canny算子求得梯度?sift算法不也是求得细节,其中也用到梯度了。这个时候我们的傅里叶变换就上场了。。。
2.我们的频率域代表:图像的变化率(简单理解梯度)
(1)傅里叶变换就是将图像从空间域--->>>频率域(值域就不说了,后者也包含值域),高频部分代表图像的细节、纹理等,低频部分代表图像的轮廓信息。比如中值滤波在空间域就是平衡那些较高的信号,在频率域就是把高频部分给过滤掉。两者是相互变化而来的,对某个的操作另一个操作也适合。
(2)变换的结果包括实数+复数(x+yi),以后用到的大部分都是将两者结合等于幅值图像显示。
(3)变换的大致意思就是任何一个函数都可以用sinx和cosx来表示,具体去看公式~~我也没太理解。。。
程序还有其它知识点,部分有注释,有部分看不懂的都在其它博客中有讲解!
上代码:
1 #include <opencv2/opencv.hpp>
2 #include <iostream>
3 #include <windows.h>
4
5 using namespace cv;
6 using namespace std;
7 void DFT(Mat& src, Mat& dst);
8 int main(int argc, char**argv)
9 {
10 Mat input_image,output_image;
11 input_image = imread("2.jpg");
12 if (input_image.data == NULL) {
13 return -1; cout << "can't open image.../";
14 }
15 DFT(input_image, output_image);
16
17 imshow("input_image2", input_image);
18 imshow("input_image2", output_image);
19 waitKey(0);
20 return 0;
21 }
22 void DFT(Mat& src, Mat& dst123)
23 {
24 Mat dst,wjy = src.clone();
25 cvtColor(src, src, CV_BGR2GRAY);
26 //----获得有利于DFT变换的尺寸--->>就是扩大成奇数尺寸
27 const int height = getOptimalDFTSize(src.rows);
28 const int width = getOptimalDFTSize(src.cols);
29 //----为扩大的尺寸赋值为0
30 Mat middle_image;
31 copyMakeBorder(src, middle_image, 0, height - src.rows, 0, width - src.cols, BORDER_CONSTANT, Scalar::all(0));
32 //----DFT变换的结果为:实数+虚数,需要弄一个二通道Mat来存储
33 //Mat channels[] = { Mat_<float>(dst),Mat::zeros(src.size(),CV_32F) };
34 vector<Mat> channels(2);
35 channels[0] = Mat_<float>(middle_image);
36 Mat temp = Mat::zeros(middle_image.size(), CV_32F);
37 channels[1] = temp;
38 Mat complexI;
39 merge(channels,complexI);
40 dft(complexI, complexI);//DFT变换
41 split(complexI, channels);//分离变换的结果:实数+虚数,两个通道
42 magnitude(channels[0], channels[1], channels[0]);//幅值计算-->>结果在channels[3]
43 dst = channels[0];
44 //---由于幅值太大无法显示,需要进行尺度(详细看sift算法)变换
45 //---这里使用 M1 = log( M + 1 )进行尺寸缩小
46 dst += Scalar::all(1);
47 log(dst, dst);
48 dst = dst(Rect(0, 0, src.cols, src.rows));
49 normalize(dst, dst, 0, 255, NORM_MINMAX);//浮点数直接显示不出来(如果是0-1可以显示)
50 dst.convertTo(dst, CV_8UC1);
51 //------频域移动
52 int cx = dst.cols;
53 int cy = dst.rows;
54 Mat top_lf = dst(Rect(Point(0, 0), Point(cx / 2, cy / 2)));
55 Mat top_rt = dst(Rect(Point(cx / 2, 0), Point(cx, cy / 2)));
56 Mat bot_lf = dst(Rect(Point(0, cy/2 ), Point(cx/2 , cy)));
57 Mat bot_rt = dst(Rect(Point(cx/2 , cy/2 ), Point(cx, cy)));
58 Mat mid;
59 //top_left<<--->>bottom_right
60 top_lf.copyTo(mid);
61 bot_rt.copyTo(top_lf);
62 mid.copyTo(bot_rt);
63 //top_right<<--->>bottom_left
64 top_rt.copyTo(mid);
65 bot_lf.copyTo(top_rt);
66 mid.copyTo(bot_lf);
67 //直线检测
68 //cvtColor(dst, dst, CV_BGR2GRAY);
69 threshold(dst, dst, 150, 255, THRESH_BINARY_INV);
70 //---此处不适合用houlinesP(),因为斜率在计算很麻烦
71 /*vector<Vec4i> lines;
72 HoughLinesP(dst, lines, 1, CV_PI / 360, 15, 3, 5);
73 Mat LineImage = Mat::zeros(dst.size(), dst.type());
74 for (size_t i = 0; i < lines.size(); i++)
75 {
76 line(LineImage, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(200, 55, 205), 1, 8, 0);
77 }*/
78 // 霍夫变换
79 vector<Vec2f> lines;
80 HoughLines(dst, lines, 1, CV_PI / 180, 100, 50, 0);
81 // 检测线个数
82 std::cout << "lines.size:" << lines.size() << std::endl;
83 Mat houghMat(dst.size(), CV_8UC3);
84 houghMat.setTo(0);
85 //for (size_t i = 0; i < lines.size(); i++)
86 // // 绘制检测线
87 //{
88 // float rho = lines[i][0], theta = lines[i][1];
89 // Point pt1, pt2;
90 // double a = cos(theta), b = sin(theta);
91 // double x0 = a*rho, y0 = b*rho;
92 // pt1.x = cvRound(x0 + 1000 * (-b));
93 // pt1.y = cvRound(y0 + 1000 * (a));
94 // pt2.x = cvRound(x0 - 1000 * (-b));
95 // pt2.y = cvRound(y0 - 1000 * (a));
96 // line(houghMat, pt1, pt2, Scalar(0, 255, 0), 1, CV_AA);
97 //}
98 //cv::imshow("houghMat", houghMat);
99 float theta = 0;
100 // 检测线角度判断
101 for (size_t i = 0; i < lines.size(); i++)
102 {
103 float thetaTemp = lines[i][1] * 180 / CV_PI;
104 if (thetaTemp > 0 && thetaTemp < 90)
105 {
106 theta = thetaTemp;
107 break;
108 }
109 }
110 // 角度转换--具体见另一篇博客霍夫变换
111 float angelT = src.rows* tan(theta / 180 * CV_PI) / src.cols;
112 theta = atan(angelT) * 180 / CV_PI;
113 std::cout << "theta:" << theta << std::endl;
114
115 // 取图像中心
116 cv::Point2f centerPoint = cv::Point2f(wjy.cols / 2, wjy.rows / 2);
117 double scale = 1;
118 // 计算旋转矩阵
119 cv::Mat warpMat = getRotationMatrix2D(centerPoint, theta, scale);
120 // 仿射变换
121 cv::Mat resultImage(wjy.size(), wjy.type());
122 cv::warpAffine(wjy, resultImage,
123 warpMat, resultImage.size());
124 resultImage.copyTo(dst123);
125 }
![](https://i-blog.csdnimg.cn/blog_migrate/ca0bbbe22851275007bbd9914441ae45.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e13a96023a67e428ff870317cbc8e894.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9815abe34b63bab178e29b4ce0adc3ad.png)
参考: 贾老师opencv系列
《opencv图像处理编程实例》
http://open.163.com/movie/2013/3/K/8/M8PTB0GHI_M8RJ8VMK8.html讲解傅里叶变换的公开课