OpenCV实现Photoshop算法(一): 图像旋转

最近学习了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 }
原文地址:https://www.cnblogs.com/ybqjymy/p/13801280.html