首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-02-19 08:21
  • 2607
  • 7233
blog.csdn.net

系列文章:

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

用OpenCV实现Photoshop算法(二): 图像剪切

用OpenCV实现Photoshop算法(三): 曲线调整

用OpenCV实现Photoshop算法(四): 色阶调整

用OpenCV实现Photoshop算法(五): 亮度对比度调整

用OpenCV实现Photoshop算法(六): 变为黑白图像

用OpenCV实现Photoshop算法(七): 调整色相饱和度

用OpenCV实现Photoshop算法(八): 可选颜色

用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
  2. #include
  3. //src为原图像, dst为新图像, angle为旋转角度(正值为顺时针旋转,负值为逆时针旋转)
  4. int imageRotate1(InputArray src, OutputArray dst, double angle)
  5. {
  6. Mat input = src.getMat();
  7. if( input.empty() ) {
  8. return -1;
  9. }
  10. //得到图像大小
  11. int width = input.cols;
  12. int height = input.rows;
  13. //计算图像中心点
  14. Point2f center;
  15. center.x = width / 2.0;
  16. center.y = height / 2.0;
  17. //获得旋转变换矩阵
  18. double scale = 1.0;
  19. Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
  20. //仿射变换
  21. warpAffine( input, dst, trans_mat, Size(width, height));
  22. return 0;
  23. }


图像旋转 -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. int width = input.cols;
  10. int height = input.rows;
  11. //计算图像中心点
  12. Point2f center;
  13. center.x = width / 2.0;
  14. center.y = height / 2.0;
  15. //获得旋转变换矩阵
  16. double scale = 1.0;
  17. Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
  18. //计算新图像大小
  19. double angle1 = angle * CV_PI / 180. ;
  20. double a = sin(angle1) * scale;
  21. double b = cos(angle1) * scale;
  22. double out_width = height * fabs(a) + width * fabs(b);
  23. double out_height = width * fabs(a) + height * fabs(b);
  24. //仿射变换
  25. warpAffine( input, dst, trans_mat, Size(out_width, out_height));
  26. return 0;
  27. }

图像旋转 -17度 的结果



还是不对,新图像变大了,但图像中心点不对,需要在旋转矩阵中加入平移,在一次变换中同时完成旋转和平移,将新图像的中心点移到正确位置。 

再次修改函数为: imageRotate3()

  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. int width = input.cols;
  10. int height = input.rows;
  11. //计算图像中心点
  12. Point2f center;
  13. center.x = width / 2.0;
  14. center.y = height / 2.0;
  15. //获得旋转变换矩阵
  16. double scale = 1.0;
  17. Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
  18. //计算新图像大小
  19. double angle1 = angle * CV_PI / 180. ;
  20. double a = sin(angle1) * scale;
  21. double b = cos(angle1) * scale;
  22. double out_width = height * fabs(a) + width * fabs(b);
  23. double out_height = width * fabs(a) + height * fabs(b);
  24. //在旋转变换矩阵中加入平移量
  25. trans_mat.at<double>(0, 2) += cvRound( (out_width - width) / 2 );
  26. trans_mat.at<double>(1, 2) += cvRound( (out_height - height) / 2);
  27. //仿射变换
  28. warpAffine( input, dst, trans_mat, Size(out_width, out_height));
  29. return 0;
  30. }

这一次正确了,新图像变大了,同时图像中心点移到了新的中心点,原图像全部能显示出来。




在实际照片旋转中,我们经常采用另一种剪切形式的调整方式:图像旋转后,缩小图片,使图片各个边角均不出现黑边。 下图红框即为新图象大小,如下:


这种调整方式下,新图像大小的计算稍为有点复杂,在网上也没有找到范例,只能自己计算了。

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时,采取缩小图片的剪切方式,否则采取放大图片的方式。

  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. int width = input.cols;
  10. int height = input.rows;
  11. //计算图像中心点
  12. Point2f center;
  13. center.x = width / 2.0;
  14. center.y = height / 2.0;
  15. //获得旋转变换矩阵
  16. double scale = 1.0;
  17. Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
  18. //计算新图像大小
  19. double angle1 = angle * CV_PI / 180. ;
  20. double a = sin(angle1) * scale;
  21. double b = cos(angle1) * scale;
  22. double out_width = height * fabs(a) + width * fabs(b); //外边框长度
  23. double out_height = width * fabs(a) + height * fabs(b);//外边框高度
  24. int new_width, new_height;
  25. if ( ! isClip ) {
  26. new_width = cvRound(out_width);
  27. new_height = cvRound(out_height);
  28. } else {
  29. //calculate width and height of clip rect
  30. double angle2 = fabs(atan(height * 1.0 / width)); //即角度 b
  31. double len = width * fabs(b);
  32. double Y = len / ( 1 / fabs(tan(angle1)) + 1 / fabs(tan(angle2)) );
  33. double X = Y * 1 / fabs(tan(angle2));
  34. new_width = cvRound(out_width - X * 2);
  35. new_height= cvRound(out_height - Y * 2);
  36. }
  37. //在旋转变换矩阵中加入平移量
  38. trans_mat.at<double>(0, 2) += cvRound( (new_width - width) / 2 );
  39. trans_mat.at<double>(1, 2) += cvRound( (new_height - height) / 2);
  40. //仿射变换
  41. warpAffine( input, dst, trans_mat, Size(new_width, new_height));
  42. return 0;
  43. }

以下是 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. Mat in = src.getMat();
  9. if( in.empty() ) return 0;
  10. Mat input;
  11. //转为灰度图
  12. if ( in.type() == CV_8UC1 )
  13. input = in;
  14. else if ( in.type() == CV_8UC3 )
  15. cvtColor(in, input, CV_BGR2GRAY);
  16. else if ( in.type() == CV_8UC3 )
  17. cvtColor(in, input, CV_BGRA2GRAY);
  18. else
  19. return 0;
  20. Mat dst, cdst;
  21. //执行Canny边缘检测(检测结果为dst, 为黑白图)
  22. double threshold1 = 90;
  23. Canny(src, dst, threshold1, threshold1 * 3, 3);
  24. //将Canny边缘检测结果转化为灰度图像(cdst)
  25. cvtColor(dst, cdst, CV_GRAY2BGR);
  26. //执行霍夫线变换,检测直线
  27. vector lines; //存放检测结果的vector
  28. double minLineLength = std::min(dst.cols, dst.rows) * 0.25; //最短线长度
  29. double maxLineGap = std::min(dst.cols, dst.rows) * 0.03 ; //最小线间距
  30. int threshold = 90;
  31. HoughLinesP(dst, lines, 1, CV_PI / 180, threshold, minLineLength, maxLineGap );
  32. //分析所需变量
  33. int x1, y1, x2 , y2; //直线的两个端点
  34. int x, y; //直线的中点
  35. double angle, rotate_angle; //直线的角度,摆正直线需要旋转的角度
  36. double line_length; //直线长度
  37. double position_weighted; //直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
  38. double main_lens[2]; //用于存放最长的二条直线长度的数组 (这两条直线即是主线条)
  39. double main_angles[2];//用于存放最长的二条直线的摆正需要旋转的角度
  40. main_lens[0] = main_lens[1] = 0;
  41. main_angles[0] = main_angles[1] = 0;
  42. //逐个分析各条直线,判断哪个是主线条
  43. for( size_t i = 0; i < lines.size(); i++ ) {
  44. //取得直线的两个端点座标
  45. x1 = lines[i][0]; y1 = lines[i][1]; x2 = lines[i][2]; y2 = lines[i][3];
  46. x = (x1 + x2 ) / 2; y = (y1 + y2) / 2;
  47. //计算直线的角度
  48. angle = (x1 == x2) ? 90 : ( atan ( (y1 - y2) * 1.0 / (x2 - x1) ) ) / CV_PI * 180;
  49. //摆正直线需要旋转的角度. 如果超出可旋转的最大角度,则忽略这个线。
  50. if ( fabs(angle - 0) <= max_angle ) {
  51. rotate_angle = angle - 0;
  52. } else if ( fabs(angle - 90) <= max_angle ) {
  53. rotate_angle = angle - 90;
  54. } else {
  55. continue;
  56. }
  57. //计算线的长度
  58. line_length = sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );
  59. //计算直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
  60. position_weighted = 1;
  61. if ( x < dst.cols / 4 || x > dst.cols * 3 / 4 ) position_weighted *= 0.8;
  62. if ( x < dst.cols / 6 || x > dst.cols * 5 / 6 ) position_weighted *= 0.5;
  63. if ( x < dst.cols / 8 || x > dst.cols * 7 / 8 ) position_weighted *= 0.5;
  64. if ( y < dst.rows / 4 || y > dst.rows * 3 / 4 ) position_weighted *= 0.8;
  65. if ( y < dst.rows / 6 || y > dst.rows * 5 / 6 ) position_weighted *= 0.5;
  66. if ( y < dst.rows / 8 || y > dst.rows * 7 / 8 ) position_weighted *= 0.5;
  67. //如果 直线长度 * 位置权重 < 最小长度, 则这条线无效
  68. line_length = line_length * position_weighted;
  69. if ( line_length < minLineLength ) continue;
  70. //如果长度为前两名,则存入数据
  71. if ( line_length > main_lens[1] ) {
  72. if (line_length > main_lens[0]) {
  73. main_lens[1] = main_lens[0];
  74. main_lens[0] = line_length;
  75. main_angles[1] = main_angles[0];
  76. main_angles[0] = rotate_angle;
  77. //如果定义了 SHOW_LINE, 则将该线条画出来
  78. #ifdef SHOW_LINE
  79. line( cdst, Point(x1, y1), Point(x2, y2), Scalar(0,0,255), 3, CV_AA);
  80. #endif
  81. } else {
  82. main_lens[1] = line_length;
  83. main_angles[1] = rotate_angle;
  84. }
  85. }
  86. }
  87. //如果定义了 SHOW_LINE, 则在source_window中显示cdst
  88. #ifdef SHOW_LINE
  89. imshow(source_window, cdst);
  90. #endif
  91. //最后,分析最长的二条直线,得出结果
  92. if ( main_lens[0] > 0 ) {
  93. //如果最长的线 与 次长的线 两者长度相近,则返回两者需要旋转的角度的平均值
  94. if (main_lens[1] > 0 && (main_lens[0] - main_lens[1] / main_lens[0] < 0.2 )) {
  95. return (main_angles[0] + main_angles[1] ) / 2;
  96. } else {
  97. return main_angles[0]; //否则,返回最长的线需要旋转的角度
  98. }
  99. } else {
  100. return 0;
  101. }
  102. }


使用detectRotation()函数自动测试角度,并显示出主要线条,运行结果:


恩,有那么一点意思, 找出了几个主线条,得出旋转 -5 度,则可以摆正图片。


当然,这个 detectRotation()函数还不是很智能,可用性还有待改进。



最后, 把本文所有代码和主程序贴上来(有点长,不过方便复制)。配置好OpenCV开发环境,把代码复制下来,就可以调试了。

代码中需要说明的是:  由于opencv的滚动条只能显示正值。 本例中rotation 的 滚动条,值为100时表示旋转角度为0。 如果小于100, 表示旋转角度为负。

  1. #include
  2. #include "opencv2/core.hpp"
  3. #include "opencv2/imgproc.hpp"
  4. #include "opencv2/highgui.hpp"
  5. #include
  6. using namespace std;
  7. using namespace cv;
  8. #define SHOW_LINE
  9. #define BASE 100
  10. static string source_window = "source";
  11. static string window_name = "image rotate";
  12. static Mat src;
  13. static int rotateDegree = 0 + BASE;
  14. static int clip = 0;
  15. //图像旋转: src为原图像, dst为新图像, angle为旋转角度(正值为顺时针旋转,负值为逆时针旋转)
  16. int imageRotate1(InputArray src, OutputArray dst, double angle)
  17. {
  18. Mat input = src.getMat();
  19. if( input.empty() ) {
  20. return -1;
  21. }
  22. //得到图像大小
  23. int width = input.cols;
  24. int height = input.rows;
  25. //计算图像中心点
  26. Point2f center;
  27. center.x = width / 2.0;
  28. center.y = height / 2.0;
  29. //获得旋转变换矩阵
  30. double scale = 1.0;
  31. Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
  32. //仿射变换
  33. warpAffine( input, dst, trans_mat, Size(width, height));
  34. return 0;
  35. }
  36. //图像旋转: src为原图像, dst为新图像, angle为旋转角度
  37. int imageRotate2(InputArray src, OutputArray dst, double angle)
  38. {
  39. Mat input = src.getMat();
  40. if( input.empty() ) {
  41. return -1;
  42. }
  43. //得到图像大小
  44. int width = input.cols;
  45. int height = input.rows;
  46. //计算图像中心点
  47. Point2f center;
  48. center.x = width / 2.0;
  49. center.y = height / 2.0;
  50. //获得旋转变换矩阵
  51. double scale = 1.0;
  52. Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
  53. //计算新图像大小
  54. double angle1 = angle * CV_PI / 180. ;
  55. double a = sin(angle1) * scale;
  56. double b = cos(angle1) * scale;
  57. double out_width = height * fabs(a) + width * fabs(b);
  58. double out_height = width * fabs(a) + height * fabs(b);
  59. //仿射变换
  60. warpAffine( input, dst, trans_mat, Size(out_width, out_height));
  61. return 0;
  62. }
  63. //图像旋转: src为原图像, dst为新图像, angle为旋转角度
  64. int imageRotate3(InputArray src, OutputArray dst, double angle)
  65. {
  66. Mat input = src.getMat();
  67. if( input.empty() ) {
  68. return -1;
  69. }
  70. //得到图像大小
  71. int width = input.cols;
  72. int height = input.rows;
  73. //计算图像中心点
  74. Point2f center;
  75. center.x = width / 2.0;
  76. center.y = height / 2.0;
  77. //获得旋转变换矩阵
  78. double scale = 1.0;
  79. Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
  80. //计算新图像大小
  81. double angle1 = angle * CV_PI / 180. ;
  82. double a = sin(angle1) * scale;
  83. double b = cos(angle1) * scale;
  84. double out_width = height * fabs(a) + width * fabs(b);
  85. double out_height = width * fabs(a) + height * fabs(b);
  86. //在旋转变换矩阵中加入平移量
  87. trans_mat.at<double>(0, 2) += cvRound( (out_width - width) / 2 );
  88. trans_mat.at<double>(1, 2) += cvRound( (out_height - height) / 2);
  89. //仿射变换
  90. warpAffine( input, dst, trans_mat, Size(out_width, out_height));
  91. return 0;
  92. }
  93. //图像旋转: src为原图像, dst为新图像, angle为旋转角度, isClip表示是采取缩小图片的方式
  94. int imageRotate4(InputArray src, OutputArray dst, double angle, bool isClip)
  95. {
  96. Mat input = src.getMat();
  97. if( input.empty() ) {
  98. return -1;
  99. }
  100. //得到图像大小
  101. int width = input.cols;
  102. int height = input.rows;
  103. //计算图像中心点
  104. Point2f center;
  105. center.x = width / 2.0;
  106. center.y = height / 2.0;
  107. //获得旋转变换矩阵
  108. double scale = 1.0;
  109. Mat trans_mat = getRotationMatrix2D( center, -angle, scale );
  110. //计算新图像大小
  111. double angle1 = angle * CV_PI / 180. ;
  112. double a = sin(angle1) * scale;
  113. double b = cos(angle1) * scale;
  114. double out_width = height * fabs(a) + width * fabs(b); //外边框长度
  115. double out_height = width * fabs(a) + height * fabs(b);//外边框高度
  116. int new_width, new_height;
  117. if ( ! isClip ) {
  118. new_width = cvRound(out_width);
  119. new_height = cvRound(out_height);
  120. } else {
  121. //calculate width and height of clip rect
  122. double angle2 = fabs(atan(height * 1.0 / width)); //即角度 b
  123. double len = width * fabs(b);
  124. double Y = len / ( 1 / fabs(tan(angle1)) + 1 / fabs(tan(angle2)) );
  125. double X = Y * 1 / fabs(tan(angle2));
  126. new_width = cvRound(out_width - X * 2);
  127. new_height= cvRound(out_height - Y * 2);
  128. }
  129. //在旋转变换矩阵中加入平移量
  130. trans_mat.at<double>(0, 2) += cvRound( (new_width - width) / 2 );
  131. trans_mat.at<double>(1, 2) += cvRound( (new_height - height) / 2);
  132. //仿射变换
  133. warpAffine( input, dst, trans_mat, Size(new_width, new_height));
  134. return 0;
  135. }
  136. /**
  137. * 检测图像倾斜度
  138. * 返回值:返回0表示无检测结果,返回非0表示摆正图象需要旋转的角度(-10至10度)
  139. */
  140. double detectRotation(InputArray src)
  141. {
  142. double max_angle = 6; //可旋转的最大角度
  143. Mat in = src.getMat();
  144. if( in.empty() ) return 0;
  145. Mat input;
  146. //转为灰度图
  147. if ( in.type() == CV_8UC1 )
  148. input = in;
  149. else if ( in.type() == CV_8UC3 )
  150. cvtColor(in, input, CV_BGR2GRAY);
  151. else if ( in.type() == CV_8UC3 )
  152. cvtColor(in, input, CV_BGRA2GRAY);
  153. else
  154. return 0;
  155. Mat dst, cdst;
  156. //执行Canny边缘检测(检测结果为dst, 为黑白图)
  157. double threshold1 = 90;
  158. Canny(src, dst, threshold1, threshold1 * 3, 3);
  159. //将Canny边缘检测结果转化为灰度图像(cdst)
  160. cvtColor(dst, cdst, CV_GRAY2BGR);
  161. //执行霍夫线变换,检测直线
  162. vector lines; //存放检测结果的vector
  163. double minLineLength = std::min(dst.cols, dst.rows) * 0.25; //最短线长度
  164. double maxLineGap = std::min(dst.cols, dst.rows) * 0.03 ; //最小线间距
  165. int threshold = 90;
  166. HoughLinesP(dst, lines, 1, CV_PI / 180, threshold, minLineLength, maxLineGap );
  167. //分析所需变量
  168. int x1, y1, x2 , y2; //直线的两个端点
  169. int x, y; //直线的中点
  170. double angle, rotate_angle; //直线的角度,摆正直线需要旋转的角度
  171. double line_length; //直线长度
  172. double position_weighted; //直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
  173. double main_lens[2]; //用于存放最长的二条直线长度的数组 (这两条直线即是主线条)
  174. double main_angles[2];//用于存放最长的二条直线的摆正需要旋转的角度
  175. main_lens[0] = main_lens[1] = 0;
  176. main_angles[0] = main_angles[1] = 0;
  177. //逐个分析各条直线,判断哪个是主线条
  178. for( size_t i = 0; i < lines.size(); i++ ) {
  179. //取得直线的两个端点座标
  180. x1 = lines[i][0]; y1 = lines[i][1]; x2 = lines[i][2]; y2 = lines[i][3];
  181. x = (x1 + x2 ) / 2; y = (y1 + y2) / 2;
  182. //计算直线的角度
  183. angle = (x1 == x2) ? 90 : ( atan ( (y1 - y2) * 1.0 / (x2 - x1) ) ) / CV_PI * 180;
  184. //摆正直线需要旋转的角度. 如果超出可旋转的最大角度,则忽略这个线。
  185. if ( fabs(angle - 0) <= max_angle ) {
  186. rotate_angle = angle - 0;
  187. } else if ( fabs(angle - 90) <= max_angle ) {
  188. rotate_angle = angle - 90;
  189. } else {
  190. continue;
  191. }
  192. //计算线的长度
  193. line_length = sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );
  194. //计算直线的位置权重:靠图像中央的线权重为1, 越靠边的线权重越小
  195. position_weighted = 1;
  196. if ( x < dst.cols / 4 || x > dst.cols * 3 / 4 ) position_weighted *= 0.8;
  197. if ( x < dst.cols / 6 || x > dst.cols * 5 / 6 ) position_weighted *= 0.5;
  198. if ( x < dst.cols / 8 || x > dst.cols * 7 / 8 ) position_weighted *= 0.5;
  199. if ( y < dst.rows / 4 || y > dst.rows * 3 / 4 ) position_weighted *= 0.8;
  200. if ( y < dst.rows / 6 || y > dst.rows * 5 / 6 ) position_weighted *= 0.5;
  201. if ( y < dst.rows / 8 || y > dst.rows * 7 / 8 ) position_weighted *= 0.5;
  202. //如果 直线长度 * 位置权重 < 最小长度, 则这条线无效
  203. line_length = line_length * position_weighted;
  204. if ( line_length < minLineLength ) continue;
  205. //如果长度为前两名,则存入数据
  206. if ( line_length > main_lens[1] ) {
  207. if (line_length > main_lens[0]) {
  208. main_lens[1] = main_lens[0];
  209. main_lens[0] = line_length;
  210. main_angles[1] = main_angles[0];
  211. main_angles[0] = rotate_angle;
  212. //如果定义了 SHOW_LINE, 则将该线条画出来
  213. #ifdef SHOW_LINE
  214. line( cdst, Point(x1, y1), Point(x2, y2), Scalar(0,0,255), 3, CV_AA);
  215. #endif
  216. } else {
  217. main_lens[1] = line_length;
  218. main_angles[1] = rotate_angle;
  219. }
  220. }
  221. }
  222. //如果定义了 SHOW_LINE, 则在source_window中显示cdst
  223. #ifdef SHOW_LINE
  224. imshow(source_window, cdst);
  225. #endif
  226. //最后,分析最长的二条直线,得出结果
  227. if ( main_lens[0] > 0 ) {
  228. //如果最长的线 与 次长的线 两者长度相近,则返回两者需要旋转的角度的平均值
  229. if (main_lens[1] > 0 && (main_lens[0] - main_lens[1] / main_lens[0] < 0.2 )) {
  230. return (main_angles[0] + main_angles[1] ) / 2;
  231. } else {
  232. return main_angles[0]; //否则,返回最长的线需要旋转的角度
  233. }
  234. } else {
  235. return 0;
  236. }
  237. }
  238. static void callbackAdjust(int , void *)
  239. {
  240. Mat dst;
  241. //imageRotate1(src, dst, rotateDegree - BASE);
  242. //imageRotate2(src, dst, rotateDegree - BASE);
  243. //imageRotate3(src, dst, rotateDegree - BASE);
  244. bool isClip = ( clip == 1 );
  245. imageRotate4(src, dst, rotateDegree - BASE, isClip );
  246. imshow(window_name, dst);
  247. }
  248. int main()
  249. {
  250. src = imread("building.jpg");
  251. if ( !src.data ) {
  252. cout << "error read image" << endl;
  253. return -1;
  254. }
  255. namedWindow(source_window);
  256. imshow(source_window, src);
  257. namedWindow(window_name);
  258. createTrackbar("rotate", window_name, &rotateDegree, BASE * 2, callbackAdjust);
  259. createTrackbar("clip", window_name, &clip, 1, callbackAdjust);
  260. //自动检测旋转角度
  261. double angle = detectRotation(src);
  262. if ( angle != 0 ) {
  263. rotateDegree = angle + BASE;
  264. setTrackbarPos("rotate", window_name, rotateDegree);
  265. }
  266. callbackAdjust(0, 0);
  267. waitKey();
  268. return 0;
  269. }





系列文章:

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

用OpenCV实现Photoshop算法(二): 图像剪切

用OpenCV实现Photoshop算法(三): 曲线调整

用OpenCV实现Photoshop算法(四): 色阶调整

系列文章:

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

用OpenCV实现Photoshop算法(二): 图像剪切

用OpenCV实现Photoshop算法(三): 曲线调整

用OpenCV实现Photoshop算法(四): 色阶调整

用OpenCV实现Photoshop算法(五): 亮度对比度调整

注:本文转载自blog.csdn.net的JoStudio的文章"http://blog.csdn.net/c80486/article/details/51867128"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top