zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    遗传算法
    UVa 11584 Partitioning by Palindromes
    UVa1625 Color Length
    UVa10003 Cutting Sticks
    UVa1347 Tour
    UVa116 (单向TSP,多决策问题)
    uVa 12563 Jin Ge Jin Qu
    模糊综合评判
    Python进阶(5)_进程与线程之协程、I/O模型
    Python进阶(4)_进程与线程 (python并发编程之多进程)
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/13801280.html
Copyright © 2011-2022 走看看