最近学习了OpenCV,于是想用它实现Photoshop的主要功能,用于照片处理。
对于一张照片,PS的一般处理步骤包括:
1, 旋转图片,校正位置。
2,剪切,调整大小,重新构图。
3,调整色阶、曲线,使图片曝光正确、对比适中。
4,调整对比度、饱和度
5,印章去掉不想要的东西,液化调整形体线条
6,对于人像图片,美肤、美白
7, 用色彩平衡、可选颜色等调整色调,形成照片调性
8,加一些光效
9,锐化
以后的一系列博文将采用OpenCV逐一实现Photoshop的算法和功能, 并用计算机视觉人工智能方式,尝试超越Photoshop一点点。
本系列博文基于OpenCV, 编程语言为C++. 由于OpenCV的跨平台性,代码可以在用于Windows, Linux, 作个接口后可用于Android,IOS.
一、图像旋转
OpenCV中, 用 warpAffine() 仿射变换函数即可以实现旋转。
例如,写一个 旋转函数 imageRotate1() 如下:
1 #include <opencv2/core.hpp>
2 #include <opencv2/imgproc.hpp>
3
4 //src为原图像, dst为新图像, angle为旋转角度(正值为顺时针旋转,负值为逆时针旋转)
5 int imageRotate1(InputArray src, OutputArray dst, double angle)
6 {
7 Mat input = src.getMat();
8 if( input.empty() ) {
9 return -1;
10 }
11
12 //得到图像大小
13 int width = input.cols;
14 int height = input.rows;
15
16 //计算图像中心点
17 Point2f center;
18 center.x = width / 2.0;
19 center.y = height / 2.0;
20
21 //获得旋转变换矩阵
22 double scale = 1.0;
23 Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
24
25 //仿射变换
26 warpAffine( input, dst, trans_mat, Size(width, height));
27
28 return 0;
29 }
图像旋转 -17度 的结果
在函数 imageRotate1()中,新图像沿用原图像大小。旋转后,图像的角部被切掉了。
这样显然不正确,需要调整图像尺寸。
调整方式一: 扩大图片,将原图片包含进去,计算示意图如下:
新图片大小为: out_width = (width*cos(a)+height*sin(a); out_height = height*cos(a)+width*sin(a))
修改原函数为 imageRotate2() :
1 //图像旋转: src为原图像, dst为新图像, angle为旋转角度
2 int imageRotate2(InputArray src, OutputArray dst, double angle)
3 {
4 Mat input = src.getMat();
5 if( input.empty() ) {
6 return -1;
7 }
8
9 //得到图像大小
10 int width = input.cols;
11 int height = input.rows;
12
13 //计算图像中心点
14 Point2f center;
15 center.x = width / 2.0;
16 center.y = height / 2.0;
17
18 //获得旋转变换矩阵
19 double scale = 1.0;
20 Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
21
22 //计算新图像大小
23 double angle1 = angle * CV_PI / 180. ;
24 double a = sin(angle1) * scale;
25 double b = cos(angle1) * scale;
26 double out_width = height * fabs(a) + width * fabs(b);
27 double out_height = width * fabs(a) + height * fabs(b);
28
29 //仿射变换
30 warpAffine( input, dst, trans_mat, Size(out_width, out_height));
31
32 return 0;
33 }
图像旋转 -17度 的结果
1 //图像旋转: src为原图像, dst为新图像, angle为旋转角度
2 int imageRotate3(InputArray src, OutputArray dst, double angle)
3 {
4 Mat input = src.getMat();
5 if( input.empty() ) {
6 return -1;
7 }
8
9 //得到图像大小
10 int width = input.cols;
11 int height = input.rows;
12
13 //计算图像中心点
14 Point2f center;
15 center.x = width / 2.0;
16 center.y = height / 2.0;
17
18 //获得旋转变换矩阵
19 double scale = 1.0;
20 Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
21
22 //计算新图像大小
23 double angle1 = angle * CV_PI / 180. ;
24 double a = sin(angle1) * scale;
25 double b = cos(angle1) * scale;
26 double out_width = height * fabs(a) + width * fabs(b);
27 double out_height = width * fabs(a) + height * fabs(b);
28
29 //在旋转变换矩阵中加入平移量
30 trans_mat.at<double>(0, 2) += cvRound( (out_width - width) / 2 );
31 trans_mat.at<double>(1, 2) += cvRound( (out_height - height) / 2);
32
33 //仿射变换
34 warpAffine( input, dst, trans_mat, Size(out_width, out_height));
35
36 return 0;
37 }
这一次正确了,新图像变大了,同时图像中心点移到了新的中心点,原图像全部能显示出来。
在实际照片旋转中,我们经常采用另一种剪切形式的调整方式:图像旋转后,缩小图片,使图片各个边角均不出现黑边。 下图红框即为新图象大小,如下:
这种调整方式下,新图像大小的计算稍为有点复杂,在网上也没有找到范例,只能自己计算了。
1,如上,旋转后的外边框大小为: out_width =(width*cos(a)+height*sin(a); out_height = height*cos(a)+width*sin(a))
2, 画几根辅助线,如下图:(注意右边图中的粉红三角形)
其最长的边长 len = width*cos(a)
角a 即旋转角度
由于外边框大小已知,则角b 可计算出来。
求解 Y: Y = len / ( 1 / tan( a ) + 1 / tan( b ) )
X = Y * 1 / tan( b )
最后求得 红框的长、宽为: new_width = out_width - 2 * X; new_height = out_height - 2 * Y
再次修改函数为: imageRotate4()
增加了一个参数: isClip , 当isClip为true时,采取缩小图片的剪切方式,否则采取放大图片的方式。
还是不对,新图像变大了,但图像中心点不对,需要在旋转矩阵中加入平移,在一次变换中同时完成旋转和平移,将新图像的中心点移到正确位置。
再次修改函数为: imageRotate3()
1 //图像旋转: src为原图像, dst为新图像, angle为旋转角度, isClip表示是采取缩小图片的方式
2 int imageRotate4(InputArray src, OutputArray dst, double angle, bool isClip)
3 {
4 Mat input = src.getMat();
5 if( input.empty() ) {
6 return -1;
7 }
8
9 //得到图像大小
10 int width = input.cols;
11 int height = input.rows;
12
13 //计算图像中心点
14 Point2f center;
15 center.x = width / 2.0;
16 center.y = height / 2.0;
17
18 //获得旋转变换矩阵
19 double scale = 1.0;
20 Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
21
22 //计算新图像大小
23 double angle1 = angle * CV_PI / 180. ;
24 double a = sin(angle1) * scale;
25 double b = cos(angle1) * scale;
26 double out_width = height * fabs(a) + width * fabs(b); //外边框长度
27 double out_height = width * fabs(a) + height * fabs(b);//外边框高度
28
29 int new_width, new_height;
30 if ( ! isClip ) {
31 new_width = cvRound(out_width);
32 new_height = cvRound(out_height);
33 } else {
34 //calculate width and height of clip rect
35 double angle2 = fabs(atan(height * 1.0 / width)); //即角度 b
36 double len = width * fabs(b);
37 double Y = len / ( 1 / fabs(tan(angle1)) + 1 / fabs(tan(angle2)) );
38 double X = Y * 1 / fabs(tan(angle2));
39 new_width = cvRound(out_width - X * 2);
40 new_height= cvRound(out_height - Y * 2);
41 }
42
43 //在旋转变换矩阵中加入平移量
44 trans_mat.at<double>(0, 2) += cvRound( (new_width - width) / 2 );
45 trans_mat.at<double>(1, 2) += cvRound( (new_height - height) / 2);
46
47 //仿射变换
48 warpAffine( input, dst, trans_mat, Size(new_width, new_height));
49
50 return 0;
51 }
以下是 isClip为true, 旋转角度为 10 的结果,可见图片旋转了、缩小了,没有黑边
由于不注意,人们拍照时经常拍歪了,一般歪得也不多,但照片就不好看了。
因此,有这么一个问题: 能否智能判别图像是否拍歪了,如果歪了,则自动计算出要旋转摆正的角度。从而使得人们一拍照,就自动拍正。
(PS:这个功能是Photoshop没有的,如果能实现,算不算超越Photoshop一点点呢?)
解决思路是这样的:
1, 图像一般有一个或两条长直线(通常这个可能是地平线、建筑物等),且倾斜角度不大
2, 利用 OpenCV图像识别能力,识别出图中有哪些直线。
3, 分析这些直线, 如果长度足够长、且位置相对居中,选取最长的两条直线,测算摆正它所需的角度,做为返回值。
事实上,人工纠正图片的Photoshop操作方式也是这样的:我们在图中人眼找一个基准线,用“度量工具”画一条线,再点菜单“图象/ 旋转画布/ 任意角度", 则Photoshop将计算出需要旋转的角度。
尝试写了一个函数: detectRotation(), 用于自动检测摆正图像的所需的旋转角度, 如下:
1 /**
2 * 智能检测图像倾斜度
3 * 返回值:返回0表示无检测结果,返回非0表示摆正图象需要旋转的角度(-10至10度)
4 */
5 double detectRotation(InputArray src)
6 {
7 double max_angle = 6; //可旋转的最大角度
8
9 Mat in = src.getMat();
10 if( in.empty() ) return 0;
11
12 Mat input;
13
14 //转为灰度图
15 if ( in.type() == CV_8UC1 )
16 input = in;
17 else if ( in.type() == CV_8UC3 )
18 cvtColor(in, input, CV_BGR2GRAY);
19 else if ( in.type() == CV_8UC3 )
20 cvtColor(in, input, CV_BGRA2GRAY);
21 else
22 return 0;
23
24 Mat dst, cdst;
25
26 //执行Canny边缘检测(检测结果为dst, 为黑白图)
27 double threshold1 = 90;
28 Canny(src, dst, threshold1, threshold1 * 3, 3);
29
30 //将Canny边缘检测结果转化为灰度图像(cdst)
31 cvtColor(dst, cdst, CV_GRAY2BGR);
32
33 //执行霍夫线变换,检测直线
34 vector<Vec4i> lines; //存放检测结果的vector
35 double minLineLength = std::min(dst.cols, dst.rows) * 0.25; //最短线长度
36 double maxLineGap = std::min(dst.cols, dst.rows) * 0.03 ; //最小线间距
37 int threshold = 90;
38 HoughLinesP(dst, lines, 1, CV_PI / 180, threshold, minLineLength, maxLineGap );
39
40 //分析所需变量
41 int x1, y1, x2 , y2; //直线的两个端点
42 int x, y; //直线的中点
43 double angle, rotate_angle; //直线的角度,摆正直线需要旋转的角度
44 double line_length; //直线长度
45 double position_weighted; //直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
46 double main_lens[2]; //用于存放最长的二条直线长度的数组 (这两条直线即是主线条)
47 double main_angles[2];//用于存放最长的二条直线的摆正需要旋转的角度
48 main_lens[0] = main_lens[1] = 0;
49 main_angles[0] = main_angles[1] = 0;
50
51 //逐个分析各条直线,判断哪个是主线条
52 for( size_t i = 0; i < lines.size(); i++ ) {
53 //取得直线的两个端点座标
54 x1 = lines[i][0]; y1 = lines[i][1]; x2 = lines[i][2]; y2 = lines[i][3];
55 x = (x1 + x2 ) / 2; y = (y1 + y2) / 2;
56 //计算直线的角度
57 angle = (x1 == x2) ? 90 : ( atan ( (y1 - y2) * 1.0 / (x2 - x1) ) ) / CV_PI * 180;
58 //摆正直线需要旋转的角度. 如果超出可旋转的最大角度,则忽略这个线。
59 if ( fabs(angle - 0) <= max_angle ) {
60 rotate_angle = angle - 0;
61 } else if ( fabs(angle - 90) <= max_angle ) {
62 rotate_angle = angle - 90;
63 } else {
64 continue;
65 }
66
67 //计算线的长度
68 line_length = sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );
69 //计算直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
70 position_weighted = 1;
71 if ( x < dst.cols / 4 || x > dst.cols * 3 / 4 ) position_weighted *= 0.8;
72 if ( x < dst.cols / 6 || x > dst.cols * 5 / 6 ) position_weighted *= 0.5;
73 if ( x < dst.cols / 8 || x > dst.cols * 7 / 8 ) position_weighted *= 0.5;
74 if ( y < dst.rows / 4 || y > dst.rows * 3 / 4 ) position_weighted *= 0.8;
75 if ( y < dst.rows / 6 || y > dst.rows * 5 / 6 ) position_weighted *= 0.5;
76 if ( y < dst.rows / 8 || y > dst.rows * 7 / 8 ) position_weighted *= 0.5;
77
78 //如果 直线长度 * 位置权重 < 最小长度, 则这条线无效
79 line_length = line_length * position_weighted;
80 if ( line_length < minLineLength ) continue;
81
82
83
84 //如果长度为前两名,则存入数据
85 if ( line_length > main_lens[1] ) {
86 if (line_length > main_lens[0]) {
87 main_lens[1] = main_lens[0];
88 main_lens[0] = line_length;
89 main_angles[1] = main_angles[0];
90 main_angles[0] = rotate_angle;
91 //如果定义了 SHOW_LINE, 则将该线条画出来
92 #ifdef SHOW_LINE
93 line( cdst, Point(x1, y1), Point(x2, y2), Scalar(0,0,255), 3, CV_AA);
94 #endif
95 } else {
96 main_lens[1] = line_length;
97 main_angles[1] = rotate_angle;
98 }
99 }
100 }
101
102 //如果定义了 SHOW_LINE, 则在source_window中显示cdst
103 #ifdef SHOW_LINE
104 imshow(source_window, cdst);
105 #endif
106
107 //最后,分析最长的二条直线,得出结果
108 if ( main_lens[0] > 0 ) {
109 //如果最长的线 与 次长的线 两者长度相近,则返回两者需要旋转的角度的平均值
110 if (main_lens[1] > 0 && (main_lens[0] - main_lens[1] / main_lens[0] < 0.2 )) {
111 return (main_angles[0] + main_angles[1] ) / 2;
112 } else {
113 return main_angles[0]; //否则,返回最长的线需要旋转的角度
114 }
115 } else {
116 return 0;
117 }
118 }
使用detectRotation()函数自动测试角度,并显示出主要线条,运行结果:
恩,有那么一点意思, 找出了几个主线条,得出旋转 -5 度,则可以摆正图片。
当然,这个 detectRotation()函数还不是很智能,可用性还有待改进。
最后, 把本文所有代码和主程序贴上来(有点长,不过方便复制)。配置好OpenCV开发环境,把代码复制下来,就可以调试了。
代码中需要说明的是: 由于opencv的滚动条只能显示正值。 本例中rotation 的 滚动条,值为100时表示旋转角度为0。 如果小于100, 表示旋转角度为负。
1 #include <iostream>
2 #include "opencv2/core.hpp"
3 #include "opencv2/imgproc.hpp"
4 #include "opencv2/highgui.hpp"
5 #include <cmath>
6
7 using namespace std;
8 using namespace cv;
9
10
11 #define SHOW_LINE
12
13 #define BASE 100
14
15 static string source_window = "source";
16 static string window_name = "image rotate";
17 static Mat src;
18 static int rotateDegree = 0 + BASE;
19 static int clip = 0;
20
21 //图像旋转: src为原图像, dst为新图像, angle为旋转角度(正值为顺时针旋转,负值为逆时针旋转)
22 int imageRotate1(InputArray src, OutputArray dst, double angle)
23 {
24 Mat input = src.getMat();
25 if( input.empty() ) {
26 return -1;
27 }
28
29 //得到图像大小
30 int width = input.cols;
31 int height = input.rows;
32
33 //计算图像中心点
34 Point2f center;
35 center.x = width / 2.0;
36 center.y = height / 2.0;
37
38 //获得旋转变换矩阵
39 double scale = 1.0;
40 Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
41
42 //仿射变换
43 warpAffine( input, dst, trans_mat, Size(width, height));
44
45 return 0;
46 }
47
48 //图像旋转: src为原图像, dst为新图像, angle为旋转角度
49 int imageRotate2(InputArray src, OutputArray dst, double angle)
50 {
51 Mat input = src.getMat();
52 if( input.empty() ) {
53 return -1;
54 }
55
56 //得到图像大小
57 int width = input.cols;
58 int height = input.rows;
59
60 //计算图像中心点
61 Point2f center;
62 center.x = width / 2.0;
63 center.y = height / 2.0;
64
65 //获得旋转变换矩阵
66 double scale = 1.0;
67 Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
68
69 //计算新图像大小
70 double angle1 = angle * CV_PI / 180. ;
71 double a = sin(angle1) * scale;
72 double b = cos(angle1) * scale;
73 double out_width = height * fabs(a) + width * fabs(b);
74 double out_height = width * fabs(a) + height * fabs(b);
75
76 //仿射变换
77 warpAffine( input, dst, trans_mat, Size(out_width, out_height));
78
79 return 0;
80 }
81
82 //图像旋转: src为原图像, dst为新图像, angle为旋转角度
83 int imageRotate3(InputArray src, OutputArray dst, double angle)
84 {
85 Mat input = src.getMat();
86 if( input.empty() ) {
87 return -1;
88 }
89
90 //得到图像大小
91 int width = input.cols;
92 int height = input.rows;
93
94 //计算图像中心点
95 Point2f center;
96 center.x = width / 2.0;
97 center.y = height / 2.0;
98
99 //获得旋转变换矩阵
100 double scale = 1.0;
101 Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
102
103 //计算新图像大小
104 double angle1 = angle * CV_PI / 180. ;
105 double a = sin(angle1) * scale;
106 double b = cos(angle1) * scale;
107 double out_width = height * fabs(a) + width * fabs(b);
108 double out_height = width * fabs(a) + height * fabs(b);
109
110 //在旋转变换矩阵中加入平移量
111 trans_mat.at<double>(0, 2) += cvRound( (out_width - width) / 2 );
112 trans_mat.at<double>(1, 2) += cvRound( (out_height - height) / 2);
113
114 //仿射变换
115 warpAffine( input, dst, trans_mat, Size(out_width, out_height));
116
117 return 0;
118 }
119
120
121 //图像旋转: src为原图像, dst为新图像, angle为旋转角度, isClip表示是采取缩小图片的方式
122 int imageRotate4(InputArray src, OutputArray dst, double angle, bool isClip)
123 {
124 Mat input = src.getMat();
125 if( input.empty() ) {
126 return -1;
127 }
128
129 //得到图像大小
130 int width = input.cols;
131 int height = input.rows;
132
133 //计算图像中心点
134 Point2f center;
135 center.x = width / 2.0;
136 center.y = height / 2.0;
137
138 //获得旋转变换矩阵
139 double scale = 1.0;
140 Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
141
142 //计算新图像大小
143 double angle1 = angle * CV_PI / 180. ;
144 double a = sin(angle1) * scale;
145 double b = cos(angle1) * scale;
146 double out_width = height * fabs(a) + width * fabs(b); //外边框长度
147 double out_height = width * fabs(a) + height * fabs(b);//外边框高度
148
149 int new_width, new_height;
150 if ( ! isClip ) {
151 new_width = cvRound(out_width);
152 new_height = cvRound(out_height);
153 } else {
154 //calculate width and height of clip rect
155 double angle2 = fabs(atan(height * 1.0 / width)); //即角度 b
156 double len = width * fabs(b);
157 double Y = len / ( 1 / fabs(tan(angle1)) + 1 / fabs(tan(angle2)) );
158 double X = Y * 1 / fabs(tan(angle2));
159 new_width = cvRound(out_width - X * 2);
160 new_height= cvRound(out_height - Y * 2);
161 }
162
163 //在旋转变换矩阵中加入平移量
164 trans_mat.at<double>(0, 2) += cvRound( (new_width - width) / 2 );
165 trans_mat.at<double>(1, 2) += cvRound( (new_height - height) / 2);
166
167 //仿射变换
168 warpAffine( input, dst, trans_mat, Size(new_width, new_height));
169
170 return 0;
171 }
172
173 /**
174 * 检测图像倾斜度
175 * 返回值:返回0表示无检测结果,返回非0表示摆正图象需要旋转的角度(-10至10度)
176 */
177 double detectRotation(InputArray src)
178 {
179 double max_angle = 6; //可旋转的最大角度
180
181 Mat in = src.getMat();
182 if( in.empty() ) return 0;
183
184 Mat input;
185
186 //转为灰度图
187 if ( in.type() == CV_8UC1 )
188 input = in;
189 else if ( in.type() == CV_8UC3 )
190 cvtColor(in, input, CV_BGR2GRAY);
191 else if ( in.type() == CV_8UC3 )
192 cvtColor(in, input, CV_BGRA2GRAY);
193 else
194 return 0;
195
196 Mat dst, cdst;
197
198 //执行Canny边缘检测(检测结果为dst, 为黑白图)
199 double threshold1 = 90;
200 Canny(src, dst, threshold1, threshold1 * 3, 3);
201
202 //将Canny边缘检测结果转化为灰度图像(cdst)
203 cvtColor(dst, cdst, CV_GRAY2BGR);
204
205 //执行霍夫线变换,检测直线
206 vector<Vec4i> lines; //存放检测结果的vector
207 double minLineLength = std::min(dst.cols, dst.rows) * 0.25; //最短线长度
208 double maxLineGap = std::min(dst.cols, dst.rows) * 0.03 ; //最小线间距
209 int threshold = 90;
210 HoughLinesP(dst, lines, 1, CV_PI / 180, threshold, minLineLength, maxLineGap );
211
212 //分析所需变量
213 int x1, y1, x2 , y2; //直线的两个端点
214 int x, y; //直线的中点
215 double angle, rotate_angle; //直线的角度,摆正直线需要旋转的角度
216 double line_length; //直线长度
217 double position_weighted; //直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
218 double main_lens[2]; //用于存放最长的二条直线长度的数组 (这两条直线即是主线条)
219 double main_angles[2];//用于存放最长的二条直线的摆正需要旋转的角度
220 main_lens[0] = main_lens[1] = 0;
221 main_angles[0] = main_angles[1] = 0;
222
223 //逐个分析各条直线,判断哪个是主线条
224 for( size_t i = 0; i < lines.size(); i++ ) {
225 //取得直线的两个端点座标
226 x1 = lines[i][0]; y1 = lines[i][1]; x2 = lines[i][2]; y2 = lines[i][3];
227 x = (x1 + x2 ) / 2; y = (y1 + y2) / 2;
228 //计算直线的角度
229 angle = (x1 == x2) ? 90 : ( atan ( (y1 - y2) * 1.0 / (x2 - x1) ) ) / CV_PI * 180;
230 //摆正直线需要旋转的角度. 如果超出可旋转的最大角度,则忽略这个线。
231 if ( fabs(angle - 0) <= max_angle ) {
232 rotate_angle = angle - 0;
233 } else if ( fabs(angle - 90) <= max_angle ) {
234 rotate_angle = angle - 90;
235 } else {
236 continue;
237 }
238
239 //计算线的长度
240 line_length = sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );
241 //计算直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
242 position_weighted = 1;
243 if ( x < dst.cols / 4 || x > dst.cols * 3 / 4 ) position_weighted *= 0.8;
244 if ( x < dst.cols / 6 || x > dst.cols * 5 / 6 ) position_weighted *= 0.5;
245 if ( x < dst.cols / 8 || x > dst.cols * 7 / 8 ) position_weighted *= 0.5;
246 if ( y < dst.rows / 4 || y > dst.rows * 3 / 4 ) position_weighted *= 0.8;
247 if ( y < dst.rows / 6 || y > dst.rows * 5 / 6 ) position_weighted *= 0.5;
248 if ( y < dst.rows / 8 || y > dst.rows * 7 / 8 ) position_weighted *= 0.5;
249
250 //如果 直线长度 * 位置权重 < 最小长度, 则这条线无效
251 line_length = line_length * position_weighted;
252 if ( line_length < minLineLength ) continue;
253
254
255
256 //如果长度为前两名,则存入数据
257 if ( line_length > main_lens[1] ) {
258 if (line_length > main_lens[0]) {
259 main_lens[1] = main_lens[0];
260 main_lens[0] = line_length;
261 main_angles[1] = main_angles[0];
262 main_angles[0] = rotate_angle;
263 //如果定义了 SHOW_LINE, 则将该线条画出来
264 #ifdef SHOW_LINE
265 line( cdst, Point(x1, y1), Point(x2, y2), Scalar(0,0,255), 3, CV_AA);
266 #endif
267 } else {
268 main_lens[1] = line_length;
269 main_angles[1] = rotate_angle;
270 }
271 }
272 }
273
274 //如果定义了 SHOW_LINE, 则在source_window中显示cdst
275 #ifdef SHOW_LINE
276 imshow(source_window, cdst);
277 #endif
278
279 //最后,分析最长的二条直线,得出结果
280 if ( main_lens[0] > 0 ) {
281 //如果最长的线 与 次长的线 两者长度相近,则返回两者需要旋转的角度的平均值
282 if (main_lens[1] > 0 && (main_lens[0] - main_lens[1] / main_lens[0] < 0.2 )) {
283 return (main_angles[0] + main_angles[1] ) / 2;
284 } else {
285 return main_angles[0]; //否则,返回最长的线需要旋转的角度
286 }
287 } else {
288 return 0;
289 }
290 }
291
292
293 static void callbackAdjust(int , void *)
294 {
295 Mat dst;
296
297 //imageRotate1(src, dst, rotateDegree - BASE);
298 //imageRotate2(src, dst, rotateDegree - BASE);
299 //imageRotate3(src, dst, rotateDegree - BASE);
300
301 bool isClip = ( clip == 1 );
302 imageRotate4(src, dst, rotateDegree - BASE, isClip );
303
304 imshow(window_name, dst);
305 }
306
307
308 int main()
309 {
310 src = imread("building.jpg");
311
312 if ( !src.data ) {
313 cout << "error read image" << endl;
314 return -1;
315 }
316
317 namedWindow(source_window);
318 imshow(source_window, src);
319
320 namedWindow(window_name);
321 createTrackbar("rotate", window_name, &rotateDegree, BASE * 2, callbackAdjust);
322 createTrackbar("clip", window_name, &clip, 1, callbackAdjust);
323
324 //自动检测旋转角度
325 double angle = detectRotation(src);
326 if ( angle != 0 ) {
327 rotateDegree = angle + BASE;
328 setTrackbarPos("rotate", window_name, rotateDegree);
329 }
330
331 callbackAdjust(0, 0);
332
333 waitKey();
334
335 return 0;
336
337 }